PROGETTO METEO AUSTRALIA

A cura di

Rick

Morty


Introduzione

Il dataset che andremo ad analizzare contiene le osservazioni giornaliere del meteo, ricavate dalle rilevazioni effettuate da varie stazioni metereologiche Australiane. L'obiettivo di questo progetto è di addestrare un classificatore binario, in modo che riesca a predire se il giorno dopo pioverà oppure no.

Le analisi del nostro progetto si dividono, principalmente, in tre fasi:

  1. Pre-processing.
  2. Classificazione.
  3. Post-processing.

Le prime due sono le fasi principali, nelle quali creiamo la versione finale del dataset e tramite esso otteniamo vari modelli di classificazione. L'ultima fase, è utile per modificare degli aspetti del dataset in modo da ottenere dei risultati di classificazione migliori. Il post-processing viene effettuato solamente sui classificatori che hanno ottenuto i risultati migliori. Quindi, questa fase è come se fosse una sotto-sezione della fase di classificazione.


Indice

1. Pre-processing

  1.1 Informazioni generali del dataset
  1.2 Analisi, statistiche e correlazione tra gli attributi
  1.3 Discretizzazione 
  1.4 Dataset finale


2. Modelli di classificazione

  2.1 Clustering (Extra)
  2.2 Vari modelli di classificazione
     2.2.1 Training e Performance dei modelli
     2.2.2 Ensemble
     2.2.3 Post-Processing
  2.3 Rete neurale
     2.3.1 Training e Performance
     2.3.2 Post-Processing

3. Conclusioni


Pre-processing

In questa prima sezione andremo ad analizzare il dataset e ad effettuare delle statistiche sugli attributi. Cercando di capire quali attributi sono importanti, quali bisogna modificare, affinchè si possano ottenere dei buoni modelli di classificazione, e quali di essi vadano eliminati.

1.1 Informazioni generali del dataset

Per iniziare, importo il dataset e cerco di ottenere semplici informazioni riguardo la dimensione e statistiche sulle colonne.

In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

plt.style.use('seaborn-notebook')

df = pd.read_csv('weatherAUS.csv')
df.head(5)
Out[1]:
Date Location MinTemp MaxTemp Rainfall Evaporation Sunshine WindGustDir WindGustSpeed WindDir9am ... Humidity3pm Pressure9am Pressure3pm Cloud9am Cloud3pm Temp9am Temp3pm RainToday RISK_MM RainTomorrow
0 2008-12-01 Albury 13.4 22.9 0.6 NaN NaN W 44.0 W ... 22.0 1007.7 1007.1 8.0 NaN 16.9 21.8 No 0.0 No
1 2008-12-02 Albury 7.4 25.1 0.0 NaN NaN WNW 44.0 NNW ... 25.0 1010.6 1007.8 NaN NaN 17.2 24.3 No 0.0 No
2 2008-12-03 Albury 12.9 25.7 0.0 NaN NaN WSW 46.0 W ... 30.0 1007.6 1008.7 NaN 2.0 21.0 23.2 No 0.0 No
3 2008-12-04 Albury 9.2 28.0 0.0 NaN NaN NE 24.0 SE ... 16.0 1017.6 1012.8 NaN NaN 18.1 26.5 No 1.0 No
4 2008-12-05 Albury 17.5 32.3 1.0 NaN NaN W 41.0 ENE ... 33.0 1010.8 1006.0 7.0 8.0 17.8 29.7 No 0.2 No

5 rows × 24 columns

In totale sono presenti 24 attributi, la cui descrizione di ognuno di essi è riportata qui sotto:

  • Date - La data di osservazione.
  • Location - Il nome del luogo in cui è situata la stazione metereologica.
  • MinTemp - Il valore (in gradi Celsius) più basso che assume la temperatura nell'arco della giornata.
  • MaxTemp - Il valore (in gradi Celsius) più alto che assume la temperatura nell'arco della giornata.
  • Rainfall - La quantità di pioggia caduta durante il giorno, misurata in mm.
  • Evaporation - La quantità di pioggia evaporata durante il giorno (mm).
  • Sunshine - Numero giornaliero di ore di sole.
  • WindGustDir - La direzione del vento più forte della giornata.
  • WindGustSpeed - La velocità (km/h) del vento più forte della giornata.
  • WindDir9am - Direzione del vento delle 9 del mattino.
  • WindDir3pm - Direzione del vento delle 3 del pomeriggio.
  • WindSpeed9am - La velocità (km/h) del vento delle 9 del mattino.
  • WindSpeed3pm - La velocità (km/h) del vento delle 3 del pomeriggio.
  • Humidity9am - L'umidità (in percentuale) presente alle 9 del mattino.
  • Humidity3pm - L'umidità (in percentuale) presente alle 3 del pomeriggio.
  • Pressure9am - La pressione atmosferica (hpa) ridotta al livello del mare alle 9 del mattino.
  • Pressure3pm - La pressione atmosferica (hpa) ridotta al livello del mare alle 3 del pomeriggio.
  • Cloud9am - Frazione del cielo oscurata dalle nuvole alle nove del mattino. Quest'attributo presenta i valori della scala "okta", che stima quanti ottavi di cielo sono oscurati dalle nuvole. Lo 0 indica che il cielo è sereno, mentre il valore 8 indica cielo completamente nuvoloso.
  • Cloud3pm - Frazione del cielo oscurata dalle nuvole alle 3 del pomeriggio.
  • Temp9am - Il valore (in gradi Celsius) della temperatura alle 9 del mattino.
  • Temp3pm - Il valore (in gradi Celsius) della temperatura alle 9 del mattino.
  • RainToday - Assumo dei valori booleani: Yes se piove durante il giorno, se il RainFall supera 1 mm di pioggia, 0 altrimenti.
  • RISK_MM - E' una misura di rischio che è stata aggiunta successivamente, del fornitore del dataset. Non è chiaro come sia stata ricavata, tanto che il fornitre stesso suggerisce di eliminarla.
  • RainTomorrow - L'attributo che rappresenta la classe che andremo a predirre. Assume solo due valori, che sono i valori della classe: Yes o No.

Ora controllo la dimensione del dataset, per capire con quante entry abbiamo a che fare.

In [2]:
df.shape
Out[2]:
(142193, 24)

Verifico quante entries sono presenti per ciascun attributo, così da verificare la presenza di valori nulli.

In [3]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 142193 entries, 0 to 142192
Data columns (total 24 columns):
Date             142193 non-null object
Location         142193 non-null object
MinTemp          141556 non-null float64
MaxTemp          141871 non-null float64
Rainfall         140787 non-null float64
Evaporation      81350 non-null float64
Sunshine         74377 non-null float64
WindGustDir      132863 non-null object
WindGustSpeed    132923 non-null float64
WindDir9am       132180 non-null object
WindDir3pm       138415 non-null object
WindSpeed9am     140845 non-null float64
WindSpeed3pm     139563 non-null float64
Humidity9am      140419 non-null float64
Humidity3pm      138583 non-null float64
Pressure9am      128179 non-null float64
Pressure3pm      128212 non-null float64
Cloud9am         88536 non-null float64
Cloud3pm         85099 non-null float64
Temp9am          141289 non-null float64
Temp3pm          139467 non-null float64
RainToday        140787 non-null object
RISK_MM          142193 non-null float64
RainTomorrow     142193 non-null object
dtypes: float64(17), object(7)
memory usage: 26.0+ MB

Descrizione generale delle statistiche del dataset.

In [4]:
df.describe()
Out[4]:
MinTemp MaxTemp Rainfall Evaporation Sunshine WindGustSpeed WindSpeed9am WindSpeed3pm Humidity9am Humidity3pm Pressure9am Pressure3pm Cloud9am Cloud3pm Temp9am Temp3pm RISK_MM
count 141556.000000 141871.000000 140787.000000 81350.000000 74377.000000 132923.000000 140845.000000 139563.000000 140419.000000 138583.000000 128179.000000 128212.000000 88536.000000 85099.000000 141289.000000 139467.000000 142193.000000
mean 12.186400 23.226784 2.349974 5.469824 7.624853 39.984292 14.001988 18.637576 68.843810 51.482606 1017.653758 1015.258204 4.437189 4.503167 16.987509 21.687235 2.360682
std 6.403283 7.117618 8.465173 4.188537 3.781525 13.588801 8.893337 8.803345 19.051293 20.797772 7.105476 7.036677 2.887016 2.720633 6.492838 6.937594 8.477969
min -8.500000 -4.800000 0.000000 0.000000 0.000000 6.000000 0.000000 0.000000 0.000000 0.000000 980.500000 977.100000 0.000000 0.000000 -7.200000 -5.400000 0.000000
25% 7.600000 17.900000 0.000000 2.600000 4.900000 31.000000 7.000000 13.000000 57.000000 37.000000 1012.900000 1010.400000 1.000000 2.000000 12.300000 16.600000 0.000000
50% 12.000000 22.600000 0.000000 4.800000 8.500000 39.000000 13.000000 19.000000 70.000000 52.000000 1017.600000 1015.200000 5.000000 5.000000 16.700000 21.100000 0.000000
75% 16.800000 28.200000 0.800000 7.400000 10.600000 48.000000 19.000000 24.000000 83.000000 66.000000 1022.400000 1020.000000 7.000000 7.000000 21.600000 26.400000 0.800000
max 33.900000 48.100000 371.000000 145.000000 14.500000 135.000000 130.000000 87.000000 100.000000 100.000000 1041.000000 1039.600000 9.000000 9.000000 40.200000 46.700000 371.000000

Le statistiche di risk_MM sono quasi equivalenti a Rainfall, meglio verificare una loro possibile correlazione. Inoltre il 75% dei dati di rainfall è prossimo allo 0, potrebbe essere utile discretizzarlo. Prima di fare tutto questo, controllo le colonne con il maggior numero di valori nulli. Ovvero, verifico in percentuale quanti nan sono presenti nelle corrispondenti colonne.

In [5]:
pd.set_option("max_columns",24)
pd.DataFrame(df.isnull().sum()/df.shape[0]).T
Out[5]:
Date Location MinTemp MaxTemp Rainfall Evaporation Sunshine WindGustDir WindGustSpeed WindDir9am WindDir3pm WindSpeed9am WindSpeed3pm Humidity9am Humidity3pm Pressure9am Pressure3pm Cloud9am Cloud3pm Temp9am Temp3pm RainToday RISK_MM RainTomorrow
0 0.0 0.0 0.00448 0.002265 0.009888 0.42789 0.476929 0.065615 0.065193 0.070418 0.02657 0.00948 0.018496 0.012476 0.025388 0.098556 0.098324 0.377353 0.401525 0.006358 0.019171 0.009888 0.0 0.0

Evaporation e sunshine presentano troppi nan, meglio eliminarli per evitare di falsare le future analisi. Droperò anche le righe che presentano tutti valori null.

In [6]:
df.dropna(axis = 0, how = 'all')
df = df.drop('Evaporation', axis = 1)
df = df.drop('Sunshine', axis = 1)
df.shape
Out[6]:
(142193, 22)

1.2 Analisi, statistiche e correlazione tra gli attributi

Le prime analisi, verranno effettuate sugli attributi che presentano maggiori valori nun e tra quelli che presentavano statistiche simili. Infatti verifico la correlazione tra Rainfall e tutti gli altri attributi, ma in particolare verifico, se è presente una forte correlazione con Risk_MM. Dato che in precedenza questi due attributi presentavano le stesse statistiche.

In [7]:
mtxcorr = df.corr()
mtxcorr["Rainfall"].sort_values(ascending=False)
Out[7]:
Rainfall         1.000000
RISK_MM          0.308557
Humidity3pm      0.255312
Humidity9am      0.223725
Cloud9am         0.198195
Cloud3pm         0.171993
WindGustSpeed    0.133497
MinTemp          0.104255
WindSpeed9am     0.086816
WindSpeed3pm     0.057759
Temp9am          0.011477
MaxTemp         -0.074839
Temp3pm         -0.079178
Pressure3pm     -0.126728
Pressure9am     -0.168085
Name: Rainfall, dtype: float64

Correlazione bassa, però i valori differiscono di poco; considerando le statistiche quasi equivalenti (cambia la media ma di poco). Quindi ho deciso di dropparla, considerando che anche la descrizione di Kaggle consiglia di farlo; essendo una collona aggiunta quando hanno messo insieme i dati, e non è chiaro come l'abbiano ricavata. Meglio non rischiare ed eliminiamola.

In [8]:
df=df.drop('RISK_MM',axis=1)
df.shape
Out[8]:
(142193, 21)

Altri due attributi presentano un'elevata percentuale di valori nulli, 40% circa ,ma sono solamente cloud3pm e cloud9am. Leggendo la descrizione su kaggle il valore delle nuvole è rappresentato in scale okta, nella quale nan indica nil significant cloud, ovvero che ci sono le nuvole, ma la loro presenza non è significativa. Quindi nel nostro caso, una presenza non significativa di nuvole assume lo stesso significato di 0 (niente nuvole), nel nostro caso della classificazione. Ipotizziamo che nan non sia un campionamento non effetuato, ma un valore della scala okta come detto poc'anzi. Se per caso la nostra ipotesi si dovesse rivelare errata, ovvero il valore della classificazione dovesse risultare non soddisfacente, provvederemo ad eliminare questi attributi e verificheremo se la classificazione avrà dei miglioramenti oppure no.

In [9]:
df.Cloud3pm.unique()
Out[9]:
array([nan,  2.,  8.,  7.,  1.,  5.,  4.,  6.,  3.,  0.,  9.])
In [10]:
df.Cloud9am.unique()
Out[10]:
array([ 8., nan,  7.,  1.,  0.,  5.,  4.,  2.,  6.,  3.,  9.])

I valori assunti dalle due colonne rispecchiano i valori della scala okta. Prima di rimpiazzare i nan con 0, vorrei analizzare i casi in cui è presente il numero 9. Perchè il 9 indica cielo oscurato da nebbia o altri fenomeni atmosferici. Quindi questo presenta delle anomalie atmosferiche, che potrebbero essere delle anomalie anche nel nostro dataset o dei semplici utliers.

In [11]:
df[df['Cloud3pm']==9]
Out[11]:
Date Location MinTemp MaxTemp Rainfall WindGustDir WindGustSpeed WindDir9am WindDir3pm WindSpeed9am WindSpeed3pm Humidity9am Humidity3pm Pressure9am Pressure3pm Cloud9am Cloud3pm Temp9am Temp3pm RainToday RainTomorrow
104342 2012-11-02 Woomera 10.6 24.6 0.2 SSE 39.0 SE ESE 26.0 17.0 45.0 13.0 1019.0 1015.2 6.0 9.0 14.8 23.7 No No
In [12]:
df[df['Cloud9am']==9]
Out[12]:
Date Location MinTemp MaxTemp Rainfall WindGustDir WindGustSpeed WindDir9am WindDir3pm WindSpeed9am WindSpeed3pm Humidity9am Humidity3pm Pressure9am Pressure3pm Cloud9am Cloud3pm Temp9am Temp3pm RainToday RainTomorrow
30097 2009-09-23 Sydney 17.0 21.8 4.4 NaN NaN W W 30.0 37.0 40.0 36.0 996.8 997.8 9.0 3.0 17.3 20.9 Yes No
45981 2012-05-27 Canberra 0.6 14.5 0.0 SW 30.0 NNW SSW 4.0 9.0 99.0 41.0 1022.4 1020.7 9.0 NaN 4.4 14.2 No No

Potrebbero essere degli outliers però il valore 9 è presente solo a uno dei due degli attributi e mai in contemporanea, quindi probabilmente non falsificano le analisi.C'è addirittura una correlazione, che quando è presente il 9 è sempre NO l'etichetta della piogga. Anche se non è significativa come correlazione dato che sono solo 3 casi.

In [13]:
#riempio i nan con 0
df['Cloud3pm'].fillna(0,inplace=True)
df['Cloud9am'].fillna(0,inplace=True)
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 142193 entries, 0 to 142192
Data columns (total 21 columns):
Date             142193 non-null object
Location         142193 non-null object
MinTemp          141556 non-null float64
MaxTemp          141871 non-null float64
Rainfall         140787 non-null float64
WindGustDir      132863 non-null object
WindGustSpeed    132923 non-null float64
WindDir9am       132180 non-null object
WindDir3pm       138415 non-null object
WindSpeed9am     140845 non-null float64
WindSpeed3pm     139563 non-null float64
Humidity9am      140419 non-null float64
Humidity3pm      138583 non-null float64
Pressure9am      128179 non-null float64
Pressure3pm      128212 non-null float64
Cloud9am         142193 non-null float64
Cloud3pm         142193 non-null float64
Temp9am          141289 non-null float64
Temp3pm          139467 non-null float64
RainToday        140787 non-null object
RainTomorrow     142193 non-null object
dtypes: float64(14), object(7)
memory usage: 22.8+ MB

Le due colonne riguardanti le nuvole non presentano più valori nulli. 142193 valori non-null come le entries del dataset.

Adesso effettuo delle analisi statistiche, riguradanti la probabilità di pioggia quando è presente un determinato valore degli attributi Cloud9am e Cloud3pm.

In [14]:
# vediamo che succede quando c'è presenza di nuvole
df_Cloud = df.groupby(['Cloud9am', 'RainTomorrow'])[['Date']].count()
df_Cloud = df_Cloud.rename({'Date': 'count'}, axis=1)
df_Cloud['prob'] = df_Cloud['count'] / df.groupby('Cloud9am').count()['RainTomorrow']
df_Cloud 
Out[14]:
count prob
Cloud9am RainTomorrow
0.0 No 50756 0.815436
Yes 11488 0.184564
1.0 No 14259 0.916506
Yes 1299 0.083494
2.0 No 5535 0.859205
Yes 907 0.140795
3.0 No 4872 0.832251
Yes 982 0.167749
4.0 No 3495 0.799223
Yes 878 0.200777
5.0 No 4281 0.776951
Yes 1229 0.223049
6.0 No 5946 0.736620
Yes 2126 0.263380
7.0 No 13327 0.674819
Yes 6422 0.325181
8.0 No 7843 0.545069
Yes 6546 0.454931
9.0 No 2 1.000000

Non è molto correlato con la pioggia, l'unico dato significativo l'abbiamo con 8, che indica pioggia quasi al 50%; già con 7 piove al 32% e man mano che diminuisce il valore diminuisce sempre di più la probabilità di pioggia.

In [15]:
df_Cloud3 = df.groupby(['Cloud3pm', 'RainTomorrow'])[['Date']].count()
df_Cloud3 = df_Cloud3.rename({'Date': 'count'}, axis=1)
df_Cloud3['prob'] = df_Cloud3['count'] / df.groupby('Cloud3pm').count()['RainTomorrow']
df_Cloud3 
Out[15]:
count prob
Cloud3pm RainTomorrow
0.0 No 50426 0.812654
Yes 11625 0.187346
1.0 No 13995 0.943886
Yes 832 0.056114
2.0 No 6496 0.908150
Yes 657 0.091850
3.0 No 5950 0.870392
Yes 886 0.129608
4.0 No 4318 0.821850
Yes 936 0.178150
5.0 No 5390 0.799347
Yes 1353 0.200653
6.0 No 6621 0.746533
Yes 2248 0.253467
7.0 No 11564 0.640594
Yes 6488 0.359406
8.0 No 5555 0.447731
Yes 6852 0.552269
9.0 No 1 1.000000

Statistiche simili rispetto a prima anche con l'attributo Cloud3pm, anche se la probabilità di pioggià è leggermente più alta.

Dato che i valori delle nuvole sono correlati tra di loro, presentano le stesse statistiche e la stessa probabilità di pioggia, faccio un'unica colonna che presenta la media dei due valori. Sarà dato maggiore credito alle nuvole presenti alle 9pm.

In [16]:
df['Cloud3pm']= ((df.Cloud3pm*8)+(df.Cloud9am*2))//10  #calcolo pesato 80% di importanza alle 3 del pomeriggio rispetto alle 8 del mattino
df['Cloud3pm']
Out[16]:
0         1.0
1         0.0
2         1.0
3         0.0
4         7.0
5         0.0
6         0.0
7         0.0
8         0.0
9         0.0
10        0.0
11        8.0
12        8.0
13        5.0
14        0.0
15        2.0
16        2.0
17        1.0
18        0.0
19        0.0
20        0.0
21        0.0
22        1.0
23        0.0
24        0.0
25        0.0
26        0.0
27        0.0
28        8.0
29        0.0
         ... 
142163    6.0
142164    0.0
142165    0.0
142166    8.0
142167    4.0
142168    0.0
142169    0.0
142170    0.0
142171    0.0
142172    0.0
142173    0.0
142174    0.0
142175    0.0
142176    0.0
142177    0.0
142178    0.0
142179    0.0
142180    0.0
142181    0.0
142182    0.0
142183    0.0
142184    0.0
142185    0.0
142186    5.0
142187    0.0
142188    0.0
142189    0.0
142190    0.0
142191    0.0
142192    2.0
Name: Cloud3pm, Length: 142193, dtype: float64
In [17]:
df=df.rename(columns={'Cloud3pm':'Cloud'})
df=df.drop('Cloud9am',axis=1)
df.head(5)
Out[17]:
Date Location MinTemp MaxTemp Rainfall WindGustDir WindGustSpeed WindDir9am WindDir3pm WindSpeed9am WindSpeed3pm Humidity9am Humidity3pm Pressure9am Pressure3pm Cloud Temp9am Temp3pm RainToday RainTomorrow
0 2008-12-01 Albury 13.4 22.9 0.6 W 44.0 W WNW 20.0 24.0 71.0 22.0 1007.7 1007.1 1.0 16.9 21.8 No No
1 2008-12-02 Albury 7.4 25.1 0.0 WNW 44.0 NNW WSW 4.0 22.0 44.0 25.0 1010.6 1007.8 0.0 17.2 24.3 No No
2 2008-12-03 Albury 12.9 25.7 0.0 WSW 46.0 W WSW 19.0 26.0 38.0 30.0 1007.6 1008.7 1.0 21.0 23.2 No No
3 2008-12-04 Albury 9.2 28.0 0.0 NE 24.0 SE E 11.0 9.0 45.0 16.0 1017.6 1012.8 0.0 18.1 26.5 No No
4 2008-12-05 Albury 17.5 32.3 1.0 W 41.0 ENE NW 7.0 20.0 82.0 33.0 1010.8 1006.0 7.0 17.8 29.7 No No

Sono riuscito a mettere solo una colonna. Ora verifico se la correlazione è rimasta equivalente.

In [18]:
# vediamo che succede quando c'è presenza di nuvole
df_Cloud = df.groupby(['Cloud', 'RainTomorrow'])[['Date']].count()
df_Cloud = df_Cloud.rename({'Date': 'count'}, axis=1)
df_Cloud['prob'] = df_Cloud['count'] / df.groupby('Cloud').count()['RainTomorrow']
df_Cloud
Out[18]:
count prob
Cloud RainTomorrow
0.0 No 50738 0.821841
Yes 10999 0.178159
1.0 No 14255 0.912554
Yes 1366 0.087446
2.0 No 7601 0.899314
Yes 851 0.100686
3.0 No 6059 0.860409
Yes 983 0.139591
4.0 No 5532 0.809127
Yes 1305 0.190873
5.0 No 6908 0.775048
Yes 2005 0.224952
6.0 No 8188 0.680915
Yes 3837 0.319085
7.0 No 8502 0.560781
Yes 6659 0.439219
8.0 No 2533 0.395472
Yes 3872 0.604528

Correlazione rimasta equivalente.

Ora mi occupo di un'altra colonna che presenta un basso numero di valori nulli, ma potrebbe rivelarsi un attributo fondamentale. Sto parlando della colonna RainToday, alla quale è possibile sopperire alla mancanza di valori, semplicemente andando a inserire nelle celle vuote il valore della colonna di RainTomorrow del giorno precedente. Ovviamente ho controllato che il primo dato, ovvero il primo giorno, non avesse valori nulli, perchè in quel caso sarebbe impossibile sostituire il valore non avendo un giorno precedente da controllare.

In [19]:
#sostituisco i valori null di rain today controllando il valore di RainTomorrow precedente
index=df.loc[df['RainToday'].isnull(),:].index
df['RainToday'][index]=df.loc[index-1,'RainTomorrow']
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 142193 entries, 0 to 142192
Data columns (total 20 columns):
Date             142193 non-null object
Location         142193 non-null object
MinTemp          141556 non-null float64
MaxTemp          141871 non-null float64
Rainfall         140787 non-null float64
WindGustDir      132863 non-null object
WindGustSpeed    132923 non-null float64
WindDir9am       132180 non-null object
WindDir3pm       138415 non-null object
WindSpeed9am     140845 non-null float64
WindSpeed3pm     139563 non-null float64
Humidity9am      140419 non-null float64
Humidity3pm      138583 non-null float64
Pressure9am      128179 non-null float64
Pressure3pm      128212 non-null float64
Cloud            142193 non-null float64
Temp9am          141289 non-null float64
Temp3pm          139467 non-null float64
RainToday        142193 non-null object
RainTomorrow     142193 non-null object
dtypes: float64(13), object(7)
memory usage: 21.7+ MB
/home/bobo/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  This is separate from the ipykernel package so we can avoid doing imports until

Discretizzo i valori di Rain today e rain tomorrow in 1 e 0, così questi attributi passano da nominali a categorici. Effettuo questo cambio, perchè il calcolatore e gli algoritmi non lavorano bene con gli attributi nominali.

In [20]:
df['RainTomorrow'].replace({'Yes': 1,'No': 0},inplace=True)
df['RainToday'].replace({'Yes': 1,'No': 0},inplace=True)
df.head()
Out[20]:
Date Location MinTemp MaxTemp Rainfall WindGustDir WindGustSpeed WindDir9am WindDir3pm WindSpeed9am WindSpeed3pm Humidity9am Humidity3pm Pressure9am Pressure3pm Cloud Temp9am Temp3pm RainToday RainTomorrow
0 2008-12-01 Albury 13.4 22.9 0.6 W 44.0 W WNW 20.0 24.0 71.0 22.0 1007.7 1007.1 1.0 16.9 21.8 0 0
1 2008-12-02 Albury 7.4 25.1 0.0 WNW 44.0 NNW WSW 4.0 22.0 44.0 25.0 1010.6 1007.8 0.0 17.2 24.3 0 0
2 2008-12-03 Albury 12.9 25.7 0.0 WSW 46.0 W WSW 19.0 26.0 38.0 30.0 1007.6 1008.7 1.0 21.0 23.2 0 0
3 2008-12-04 Albury 9.2 28.0 0.0 NE 24.0 SE E 11.0 9.0 45.0 16.0 1017.6 1012.8 0.0 18.1 26.5 0 0
4 2008-12-05 Albury 17.5 32.3 1.0 W 41.0 ENE NW 7.0 20.0 82.0 33.0 1010.8 1006.0 7.0 17.8 29.7 0 0

Controlliamo la relazione tra raintoday e rain tomorrow. Se per caso la pioggia del giorno corrente, aumenta la probabilità di pioggia del giorno seguente.

In [21]:
df_today = df.groupby(['RainToday', 'RainTomorrow'])[['Date']].count()
df_today = df_today.rename({'Date': 'count'}, axis=1)
df_today['prob'] = df_today['count'] / df.groupby('RainToday').count()['RainTomorrow']

rain = df_today.loc[1]
no_rain= df_today.loc[0]
fig, axes = plt.subplots(1,2)
axes[0].bar(1,rain.loc[0]['prob'], label='RainTomorrow: no',align='edge',width=0.6,alpha=0.5)
axes[0].bar(1,rain.loc[1]['prob'], label='RainTomorrow: yes', align='edge',width=-0.6,alpha=0.5)

axes[1].bar(1,no_rain.loc[0]['prob'], label='RainTomorrow: no', align='edge',width=0.6,alpha=0.5)
axes[1].bar(1,no_rain.loc[1]['prob'], label='RainTomorrow: yes', align='edge',width=-0.6,alpha=0.5)

axes[0].set_title('RainToday: yes')
axes[1].set_title('RainToday: no')

axes[0].set_ylabel('Probabilità')
axes[1].set_ylabel('Probabilità')


axes[0].set_xticks([])
axes[1].set_xticks([])

axes[0].legend()
axes[1].legend()

fig.set_size_inches(14, 8)
plt.show()

RainToday influisce molto rain tomorrow nel caso in cui non piove, con 80% di probabilità che non piove nemmeno il giorno seguente.Se piove, il giorno seguente avremo una probabilità del 45% che piova. Influisce più di quanto pensassi.

Verifico la correlazione tra TUTTI gli attributi. Così verifico se qualche attributo è la combinazione di altri. Prima plotto i grafici e poi per casi specifici li verifico uno ad uno.

Controllo per primo la matrice di correlazione per tutti gli attributi. Molto probabilmente non si capirà molto, infatti dopo la vedo per singoli attributi.

In [22]:
plt.figure(figsize=(12,8))
sns.heatmap(mtxcorr,annot=True,cmap='bone',linewidths=0.25)
Out[22]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fdea97c74a8>

Già da questa matrice si nota la correlazione tra gli attributi che operano sugli stessi settori. Ad esempio notiamo la forte correlazione tra gli attributi che riguardano la temperatura, correlazione del 90% per alcuni di essi. La correlazione tra gli attributi della pressione, 96% di correlazione, e così via. Per una maggiore chiarezza verifico la correlazione degli attributi che rappresentano le stesse grandezze fisiche, tramite delle scatter matrix.

N.B. Non è presente una scatter matrix tra tutte le possibili coppie di attributi, dato che la macchina non riesce a computarla, crasha spesso se provo a farla di tutti.

Per primo controllo le ralzioni tra gli attributi riguardanti la tamperatura.

In [23]:
from pandas.plotting import scatter_matrix


attributes = ['MinTemp','MaxTemp','Temp9am','Temp3pm','RainToday','RainTomorrow']

scatter_matrix(df[attributes], figsize=(36,24),alpha=0.2)
plt.show()
In [24]:
g= sns.pairplot(df,
                 x_vars = attributes[:-2],
                 y_vars = attributes[:-2],
                 hue='RainTomorrow',
                 diag_kind='kde',
                 diag_kws={'alpha':0.2},
               )
g.fig.set_size_inches(24,12)
/home/bobo/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py:447: RuntimeWarning: invalid value encountered in greater
  X = X[np.logical_and(X > clip[0], X < clip[1])] # won't work for two columns.
/home/bobo/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py:447: RuntimeWarning: invalid value encountered in less
  X = X[np.logical_and(X > clip[0], X < clip[1])] # won't work for two columns.

Dalla figura si nota una forte relazione tra MaxTemp e Temp3pm, sembra quasi una linea retta. L'altra forte relazione è tra Temp9am e MinTemp, che è quasi evidente come la correlazione precedente. Notiamo che la piovosità del giorno stesso non influisce molto sulla piovosità del giorno dopo, però la tenamo in considerazione, per il momento, perchè potrebbe essere utile o correlata agli altri attributi. Per ora elimino le due colonne aggiuntive della temperatura.

In [25]:
df=df.drop("Temp3pm",axis=1)
df=df.drop("Temp9am",axis=1)
df.shape
Out[25]:
(142193, 18)

Adesso controllo gli attributi adibiti all'umidità.

In [26]:
attributes = ['Humidity9am','Humidity3pm','RainToday','RainTomorrow']

scatter_matrix(df[attributes], figsize=(36,24),alpha=0.2)
plt.show()
In [27]:
g= sns.pairplot(df,
                 x_vars = attributes[:-2],
                 y_vars = attributes[:-2],
                 hue='RainTomorrow',
                 diag_kind='kde',
                 diag_kws={'alpha':0.2},
               )
g.fig.set_size_inches(24,12)
/home/bobo/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py:447: RuntimeWarning: invalid value encountered in greater
  X = X[np.logical_and(X > clip[0], X < clip[1])] # won't work for two columns.
/home/bobo/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py:447: RuntimeWarning: invalid value encountered in less
  X = X[np.logical_and(X > clip[0], X < clip[1])] # won't work for two columns.

Forte correlazione tra le due

In [28]:
mtxcorr = df.corr()
mtxcorr["Humidity3pm"].sort_values(ascending=False)
Out[28]:
Humidity3pm      1.000000
Humidity9am      0.667388
RainTomorrow     0.446160
RainToday        0.376340
Cloud            0.315038
Rainfall         0.255312
Pressure3pm      0.051840
WindSpeed3pm     0.015903
MinTemp          0.005999
WindGustSpeed   -0.026663
Pressure9am     -0.027449
WindSpeed9am    -0.031607
MaxTemp         -0.509270
Name: Humidity3pm, dtype: float64

Anche in questo caso decido di unire le due colonne in una. Anche questa volta effettuo una media pesata dando più importanza all'attributo Humidity3pm, in quanto è una rilevazione che temporalmente è più vicina al giorno seguente, rispetto a quella effettuata alle 9 della mattina.

In [29]:
df['Humidity3pm']= (df.Humidity3pm*8+df.Humidity9am*2)/10
df=df.rename(columns={'Humidity3pm':'Humidity'})
df=df.drop('Humidity9am',axis=1)

#controllo se la correlazione con rain tomorrow è rimasta uguale 

mtxcorr = df.corr()
mtxcorr["Humidity"].sort_values(ascending=False)
Out[29]:
Humidity         1.000000
RainTomorrow     0.433219
RainToday        0.391869
Cloud            0.308544
Rainfall         0.262618
Pressure3pm      0.081957
Pressure9am      0.004696
WindSpeed3pm    -0.014252
MinTemp         -0.039487
WindGustSpeed   -0.066629
WindSpeed9am    -0.081256
MaxTemp         -0.536164
Name: Humidity, dtype: float64

Abbiamo aumentato la correlazione con l'attributo significativo per la classificazione, ovvero RainTomorrow. Quindi l'unine delle due colonne è stata eseguita correttamente.

Adesso verifico la pressione.

In [30]:
attributes = ['Pressure9am','Pressure3pm','RainToday','RainTomorrow']

scatter_matrix(df[attributes], figsize=(36,24),alpha=0.2)
plt.show()
In [31]:
g= sns.pairplot(df,
                 x_vars = attributes[:-2],
                 y_vars = attributes[:-2],
                 hue='RainTomorrow',
                 diag_kind='kde',
                 diag_kws={'alpha':0.2},
               )
g.fig.set_size_inches(24,12)
/home/bobo/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py:447: RuntimeWarning: invalid value encountered in greater
  X = X[np.logical_and(X > clip[0], X < clip[1])] # won't work for two columns.
/home/bobo/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py:447: RuntimeWarning: invalid value encountered in less
  X = X[np.logical_and(X > clip[0], X < clip[1])] # won't work for two columns.

Anche in questo caso forte correlazione tra le due colonne.

In [32]:
mtxcorr = df.corr()
mtxcorr["Pressure3pm"].sort_values(ascending=False)
Out[32]:
Pressure3pm      1.000000
Pressure9am      0.961348
Humidity         0.081957
Cloud           -0.085609
RainToday       -0.104676
Rainfall        -0.126728
WindSpeed9am    -0.174916
RainTomorrow    -0.226031
WindSpeed3pm    -0.254988
WindGustSpeed   -0.412922
MaxTemp         -0.427279
MinTemp         -0.461623
Name: Pressure3pm, dtype: float64

Attributi quasi equivalenti, per evitare questa ridondanza li unisco in una sola colonna effettuando la media aritmetica dato che i due valori sono quasi uguali

In [33]:
df['Pressure3pm']= (df.Pressure3pm+df.Pressure9am)/2
df=df.rename(columns={'Pressure3pm':'Pressure'})
df=df.drop('Pressure9am',axis=1)
df.shape
Out[33]:
(142193, 16)

Ultimo controllo sugli attributi del vento.

In [34]:
attributes = ['WindGustSpeed','WindSpeed9am','WindSpeed3pm','RainToday','RainTomorrow']

scatter_matrix(df[attributes], figsize=(36,24),alpha=0.2)
plt.show()
In [35]:
g= sns.pairplot(df,
                 x_vars = attributes[:-2],
                 y_vars = attributes[:-2],
                 hue='RainTomorrow',
                 diag_kind='kde',
                 diag_kws={'alpha':0.2},
               )
g.fig.set_size_inches(24,12)
/home/bobo/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py:447: RuntimeWarning: invalid value encountered in greater
  X = X[np.logical_and(X > clip[0], X < clip[1])] # won't work for two columns.
/home/bobo/anaconda3/lib/python3.7/site-packages/statsmodels/nonparametric/kde.py:447: RuntimeWarning: invalid value encountered in less
  X = X[np.logical_and(X > clip[0], X < clip[1])] # won't work for two columns.
In [36]:
mtxcorr = df.corr()
mtxcorr["WindGustSpeed"].sort_values(ascending=False)
Out[36]:
WindGustSpeed    1.000000
WindSpeed3pm     0.686419
WindSpeed9am     0.604837
RainTomorrow     0.234010
MinTemp          0.177285
RainToday        0.154148
Rainfall         0.133497
Cloud            0.101280
MaxTemp          0.067690
Humidity        -0.066629
Pressure        -0.439830
Name: WindGustSpeed, dtype: float64

SI nota che entrambi gli attributi WindSpeed sono abbastanza correlati con WindGustSpeed, il che indica che combinati potrebbero dare WindGustSpeed, dato che quest'ultimo rappresenta il cambio di intesità della velocita del vento durante la giornata. Inoltre WindSpeed3pm e WindSpeed9am, presentano una bassa correlazione con rainTomorrow rispetto a WindGustSpeed, il quale è correlato anche alla pressione. Quindi ho deciso di eliminare queste due colonne, comprese anche le colonne relative alle loro direzioni del vento.

In [37]:
df=df.drop('WindSpeed9am',axis=1)
df=df.drop('WindSpeed3pm',axis=1)
df=df.drop('WindDir9am',axis=1)
df=df.drop('WindDir3pm',axis=1)
df.shape
Out[37]:
(142193, 12)

Adesso passo ai dati non categorici, iniziando da data. Inanzitutto controllo fino a dove inizia e fino a dove finisce il periodo di osservazione, controllo anche la presenza di date ripetute.In seguito, la prima analisi statistica che effettuerò riguarda l'andamento generico della piovosità.

In [38]:
df_sort=df.sort_values(by=['Date'],ascending=True)
print("Inizio osservazione: "+str(df_sort['Date'].iloc[0]))
print("Fine osservazione: "+str(df_sort['Date'].iloc[len(df)-1]))
Inizio osservazione: 2007-11-01
Fine osservazione: 2017-06-25
In [39]:
print("Numero giorni (approssimato) di osservazione: "+str((2017-2007)*365-30*5))
Numero giorni (approssimato) di osservazione: 3500

I giorni del periodo di osservazione sono effettivamente minori del numero di entries, quindi probabilmente le date sono al loro volta divise per zona. Ovvero a una dato corrispondono più location. Provo a confermare questa ipotesi.

In [40]:
df_check = df_sort.groupby(['Location'])[['Date']].count()
df_check = df_check.rename({'Date': 'NumGiorniOsservati'}, axis=1)
df_check
Out[40]:
NumGiorniOsservati
Location
Adelaide 3090
Albany 3016
Albury 3011
AliceSprings 3031
BadgerysCreek 2928
Ballarat 3028
Bendigo 3034
Brisbane 3161
Cairns 2988
Canberra 3418
Cobar 2988
CoffsHarbour 2953
Dartmoor 2943
Darwin 3192
GoldCoast 2980
Hobart 3188
Katherine 1559
Launceston 3028
Melbourne 2435
MelbourneAirport 3009
Mildura 3007
Moree 2854
MountGambier 3030
MountGinini 2907
Newcastle 2955
Nhil 1569
NorahHead 2929
NorfolkIsland 2964
Nuriootpa 3002
PearceRAAF 2762
Penrith 2964
Perth 3193
PerthAirport 3009
Portland 2996
Richmond 2951
Sale 3000
SalmonGums 2955
Sydney 3337
SydneyAirport 3005
Townsville 3033
Tuggeranong 2998
Uluru 1521
WaggaWagga 2976
Walpole 2819
Watsonia 2999
Williamtown 2553
Witchcliffe 2952
Wollongong 2983
Woomera 2990
In [41]:
dateTotali=df_check.NumGiorniOsservati.values.sum()
if dateTotali==df['Date'].count():
   print("Ipotesi Confermata!")
else :
   print("Ipotesi Errata!")
Ipotesi Confermata!

La nostra ipotesi è stata confermata, perchè il numero di giorni osservati combiacia più o meno con il numero di giorni che passano nel periodo di osservazione. Alcune località però presentano un numero significativamente più basso di valori di data, il che significa che molte zone, come Uluru,Katherine etc. non hanno riportato la loro osservazione per lunghi periodi.

Siccome non c'è uniformita nel numero di osservazioni, proverò a effettuare delle statistiche in base ai mesi in cui ha piovuto di più, in termini di giorni di pioggia, e i mesi in cui è scesa più pioggia in termini di ml.

In [42]:
#Faccio una copia del dataset per suddividere la data in mesi
import re
df_copy=df.copy()

mesi=['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno','Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre']

df_copy.Date.replace({'[0-9]+-01-[0-9]+':0,'[0-9]+-02-[0-9]+':1,'[0-9]+-03-[0-9]+':2,'[0-9]+-04-[0-9]+':3,
                     '[0-9]+-05-[0-9]+':4,'[0-9]+-06-[0-9]+':5,'[0-9]+-07-[0-9]+':6,'[0-9]+-08-[0-9]+':7,
                      '[0-9]+-09-[0-9]+':8,'[0-9]+-10-[0-9]+':9,'[0-9]+-11-[0-9]+':10,'[0-9]+-12-[0-9]+':11},
                        regex=True,inplace=True)

df_mesi=df_copy.groupby(['Date'])[['RainToday']].sum()//(30*10)
df_mesi['MediaPioggiaCaduta']=df_copy.groupby(['Date']).mean()['Rainfall']


fig, ax = plt.subplots(1,1)
fig.set_size_inches(9,9)
ax.plot(df_mesi['RainToday'],'ko--')

ax.set_xticks([i for i in range(12) ])
xlabels = ax.set_xticklabels(mesi,rotation=30, fontsize=12)
ax.set_title('Giorni di pioggia medi per ogni mese')
_ = ax.set_ylabel('Numero Giorni')
In [43]:
ig, ax = plt.subplots(1,1)
fig.set_size_inches(9,9)
ax.plot(df_mesi['MediaPioggiaCaduta'],'ko--')

ax.set_xticks([i for i in range(12) ])
xlabels = ax.set_xticklabels(mesi,rotation=30, fontsize=12)
ax.set_title('Quantità di pioggia media per ogni mese')  
_ = ax.set_ylabel('mm di pioggia')

Dalle statistiche si nota che i mesi in cui è piovuto più giorni, coprono il periodo da maggio a luglio. Mentre gli altri mesi è piovuto lo stesso numero di giorni circa, da 2200 a 2500 giorni. Nonostante questo, è caduta più pioggia, in termine di ml, da Gennaio fino a Marzo, fatta eccezione per Giugno. A noi ovviamente interessa il numero di giorni in cui è piovuto e non la quantità. ANche se questi non sono dati molto significativi, considerando che il minor numero di giorni di pioggia medi è 7, mentre il massimo è 11. Quindi i giorni di pioggia sono ben distribuiti nell'arco dei mesi.

Dato che i periodi di pioggia sono divisi per blocchi di mesi, provo a dividerli per stagioni. In modo da raggruppare i mesi in cui piove di iù in un'unica batch.

In [44]:
df_copy=df.copy()

stagioni=['Estate','Autunno','Inverno','Primavera']

df_copy.Date.replace({'[0-9]+-(12|0[1-2])-[0-9]+':0,'[0-9]+-0[3-5]-[0-9]+':1,'[0-9]+-0[6-8]-[0-9]+':2,'[0-9]+-(09|1[0-1])-[0-9]+':3},
                      regex=True,inplace=True)

df_stagioni=df_copy.groupby(['Date'])[['RainToday']].sum()//(30*3*10)
df_stagioni['MediaPioggiaCaduta']=df_copy.groupby(['Date']).mean()['Rainfall']


fig, ax = plt.subplots(1,1)
fig.set_size_inches(9,9)
ax.plot(df_stagioni['RainToday'],'ko--')

ax.set_xticks([i for i in range(4) ])
xlabels = ax.set_xticklabels(stagioni,rotation=30, fontsize=12)
ax.set_title('Giorni di pioggia medi per ogni stagione')  
_ = ax.set_ylabel('Numero Giorni')
In [45]:
ig, ax = plt.subplots(1,1)
fig.set_size_inches(9,9)
ax.plot(df_stagioni['MediaPioggiaCaduta'],'ko--')

ax.set_xticks([i for i in range(4) ])
xlabels = ax.set_xticklabels(stagioni,rotation=30, fontsize=12)
ax.set_title('Quantità di pioggia media per ogni stagione')  
_ = ax.set_ylabel('mm di pioggia')

Effettuando la statistica sulle stagioni non abbiamo ottenuto dei dati considerevoli rispetto all'analisi effettuata sui mesi. Anzi adesso ci siamo accorti che non c'è una grossa differenza tra le varie stagioni, i giorni di pioggia medi durante l'anno cambiano di poco. L'unica differenza rigurda l'ammontare di pioggia che cade, d'estate piove di meno in termini di giorni ma di più in termini di mm. Ma tutto ciò non è rilevante per gli obiettivi del nostro tipo di classificazione.

In [46]:
df.Location.unique()
Out[46]:
array(['Albury', 'BadgerysCreek', 'Cobar', 'CoffsHarbour', 'Moree',
       'Newcastle', 'NorahHead', 'NorfolkIsland', 'Penrith', 'Richmond',
       'Sydney', 'SydneyAirport', 'WaggaWagga', 'Williamtown',
       'Wollongong', 'Canberra', 'Tuggeranong', 'MountGinini', 'Ballarat',
       'Bendigo', 'Sale', 'MelbourneAirport', 'Melbourne', 'Mildura',
       'Nhil', 'Portland', 'Watsonia', 'Dartmoor', 'Brisbane', 'Cairns',
       'GoldCoast', 'Townsville', 'Adelaide', 'MountGambier', 'Nuriootpa',
       'Woomera', 'Albany', 'Witchcliffe', 'PearceRAAF', 'PerthAirport',
       'Perth', 'SalmonGums', 'Walpole', 'Hobart', 'Launceston',
       'AliceSprings', 'Darwin', 'Katherine', 'Uluru'], dtype=object)

Tutte queste zone andando a controllare su internet, fanno parte della zona del sud est dell'australia. COnsiderando che copre solo quella zona e che in alcune località mancano parecchie date, si è deciso di eliminare le località, perchè potrebbero causare overffitting, basare del training su di esse. Dato che nel momento in cui uscisse una nuova zona potrebbe essere falsata la classificazione, se si basa sull'attributo della location. Inoltre vorrei droppare anche le date, perchè, come visto dai grafici, in termine di piovosità non c'è molta differenza tra le varie stagioni o mesi. Infine, molte di esse presentano dei valori mancanti, quindi alcune statistiche sono falsate.

In [47]:
df=df.drop('Location',axis=1)
df=df.drop('Date',axis=1)
df.shape
Out[47]:
(142193, 10)

Ultimo controllo sull'attributo Wind Gust Direction.

In [48]:
df.WindGustDir.unique()
Out[48]:
array(['W', 'WNW', 'WSW', 'NE', 'NNW', 'N', 'NNE', 'SW', 'ENE', 'SSE',
       'S', 'NW', 'SE', 'ESE', nan, 'E', 'SSW'], dtype=object)
In [49]:
df_Dir = df.groupby(['WindGustDir', 'RainTomorrow'])[['WindGustSpeed']].count()
df_Dir = df_Dir.rename({'WindGustSpeed': 'count'}, axis=1)
df_Dir['prob'] = df_Dir['count'] / df.groupby('WindGustDir').count()['RainTomorrow']
df_Dir
Out[49]:
count prob
WindGustDir RainTomorrow
E 0 7721 0.851174
1 1350 0.148826
ENE 0 6704 0.838839
1 1288 0.161161
ESE 0 6105 0.835729
1 1200 0.164271
N 0 6581 0.728551
1 2452 0.271449
NE 0 5741 0.813173
1 1319 0.186827
NNE 0 4952 0.769781
1 1481 0.230219
NNW 0 4698 0.716049
1 1863 0.283951
NW 0 5719 0.714607
1 2284 0.285393
S 0 6932 0.774612
1 2017 0.225388
SE 0 7576 0.813836
1 1733 0.186164
SSE 0 7238 0.804848
1 1755 0.195152
SSW 0 6713 0.779675
1 1897 0.220325
SW 0 6993 0.794930
1 1804 0.205070
W 0 7173 0.733436
1 2607 0.266564
WNW 0 5791 0.717952
1 2275 0.282048
WSW 0 6816 0.765757
1 2085 0.234243

Valori di probabilità simili un po' per tutti i valori di wind gust direction, in particolare per quelli che procedono nella stessa direzione. Provo a discretizzarli e assegnargli valori numerici, raggruppando ogni valore nella rispettiva direzione cardinale; quindi la discretizzazione viene effettuata considerando come uniche direzioni il Nord,Sud,Est e Ovest.

In [50]:
df.WindGustDir.replace({'N[A-Z]*':0,'E[A-Z]*':1,'S[A-Z]*':2,'W[A-Z]*':3},regex=True,inplace=True)

df_Dir = df.groupby(['WindGustDir', 'RainTomorrow'])[['WindGustSpeed']].count()
df_Dir = df_Dir.rename({'WindGustSpeed': 'count'}, axis=1)
df_Dir['prob'] = df_Dir['count'] / df.groupby('WindGustDir').count()['RainTomorrow']
df_Dir
Out[50]:
count prob
WindGustDir RainTomorrow
0.0 0 40186 0.756115
1 12962 0.243885
1.0 0 28640 0.825884
1 6038 0.174116
2.0 0 27454 0.778682
1 7803 0.221318
3.0 0 7173 0.733436
1 2607 0.266564

Anche in questo caso asumono probabilità simili, prime di vedere se influisce su WindGustSpeed discretizzo tutti gli attributi su cui è possibile farlo.


Discretizzazione

Prima di concludere le nostre analisi sul dataset, controllo la distribuzione dei valori degli attributi nello spazio. Questo può essere utile per capire se ci sono intervalli che raggrupppano un gran numero di entries, se così fosse converrebbe discretizzare gli attributi per migliorare le performance.

In [51]:
df.hist(bins = 50, figsize = (20,15) )
plt.show()

Distribuzioni gaussiane, fatta eccezine per RainFall che presenta un enorme picco in zero e WindGustSpeed che present vari picchi. Questi dati ci fanno capire che, molto probabilmente, se discretizziamo i dati dovremmo ottenere ottimi risultati, considerando che per alcuni valori sono presenti un numero spropositato di istanze. Prima di fare questo utilizo una pipeline nella quale insirisco al posto dei null la media, tanto per quei valori rimasti la percentuale massima di null non supera il 6%.

In [52]:
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline

pipeline = Pipeline([
       ('imputer', SimpleImputer(strategy="median"))])

columns=df.columns
df=pipeline.fit_transform(df)

df=pd.DataFrame(df,columns=columns)
df.RainToday = df.RainToday.astype(int)
df.RainTomorrow = df.RainTomorrow.astype(int)
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 142193 entries, 0 to 142192
Data columns (total 10 columns):
MinTemp          142193 non-null float64
MaxTemp          142193 non-null float64
Rainfall         142193 non-null float64
WindGustDir      142193 non-null float64
WindGustSpeed    142193 non-null float64
Humidity         142193 non-null float64
Pressure         142193 non-null float64
Cloud            142193 non-null float64
RainToday        142193 non-null int64
RainTomorrow     142193 non-null int64
dtypes: float64(8), int64(2)
memory usage: 10.8 MB

Adesso discretizzo e verifico che dopo la discretizzazione sia tutto a posto.

In [53]:
df['Pressure'] = pd.cut(df.Pressure, bins = [970., 1000., 1010. ,1020.,1030.,np.inf], labels=[0,1,2,3,4])
tmp = df.groupby(['Pressure', 'RainTomorrow'])[['RainToday']].count()
tmp = tmp.rename({'RainToday':'count'}, axis=1)
tmp['prob'] = tmp['count']/df.groupby('Pressure').count()['RainTomorrow']
tmp
Out[53]:
count prob
Pressure RainTomorrow
0 0 462 0.330709
1 935 0.669291
1 0 12748 0.603084
1 8390 0.396916
2 0 63268 0.783049
1 17529 0.216951
3 0 30563 0.865734
1 4740 0.134266
4 0 3275 0.920461
1 283 0.079539

Ottima discretizzazione, più la pressione aumenta, più la probabilità di piovere diminuisce.

In [54]:
df['Humidity'] = pd.cut(df.Humidity, bins = [-0.1, 20., 40. ,60.,80.,np.inf], labels=[0,1,2,3,4])
tmp = df.groupby(['Humidity', 'RainTomorrow'])[['RainToday']].count()
tmp = tmp.rename({'RainToday':'count'}, axis=1)
tmp['prob'] = tmp['count']/df.groupby('Humidity').count()['RainTomorrow']
tmp
Out[54]:
count prob
Humidity RainTomorrow
0 0 5896 0.966241
1 206 0.033759
1 0 23696 0.937564
1 1578 0.062436
2 0 47609 0.870397
1 7089 0.129603
3 0 29469 0.693372
1 13032 0.306628
4 0 3646 0.267734
1 9972 0.732266

Qui avviene il contrario, più l'umidità è alta più è probabile che piova.

In [55]:
df['WindGustSpeed'] = pd.cut(df.WindGustSpeed, bins = [0., 20., 35.,40.0 ,60.,80.,np.inf], labels=[0,1,2,3,4,5])
tmp = df.groupby(['WindGustSpeed', 'RainTomorrow'])[['RainToday']].count()
tmp = tmp.rename({'RainToday':'count'}, axis=1)
tmp['prob'] = tmp['count']/df.groupby('WindGustSpeed').count()['RainTomorrow']
tmp
Out[55]:
count prob
WindGustSpeed RainTomorrow
0 0 6379 0.866830
1 980 0.133170
1 0 42677 0.849767
1 7545 0.150233
2 0 20375 0.788842
1 5454 0.211158
3 0 35515 0.735163
1 12794 0.264837
4 0 4808 0.525867
1 4335 0.474133
5 0 562 0.422239
1 769 0.577761

Più aumenta il windgustspeed più aumenta la probabilità di piovere, anche se di poco.

Adesso controllo meglio Rainfall per capire come discretizzarlo, dato che ha il 50% di valori a 0 (picco elevato).

In [56]:
ax=df.Rainfall.hist(bins = 50, figsize = (10,5))
ax.set_xticks([i for i in range(0,50,10) ])
plt.show(fig)
In [57]:
df['Rainfall'] = pd.cut(df.Rainfall, bins = [-0.1, 9.,20.,np.inf], labels=[0,1,2])
tmp = df.groupby(['Rainfall', 'RainTomorrow'])[['RainToday']].count()
tmp = tmp.rename({'RainToday':'count'}, axis=1)
tmp['prob'] = tmp['count']/df.groupby('Rainfall').count()['RainTomorrow']
tmp
Out[57]:
count prob
Rainfall RainTomorrow
0 0 106152 0.803610
1 25942 0.196390
1 0 2775 0.450707
1 3382 0.549293
2 0 1389 0.352359
1 2553 0.647641

Rainfall dopo averlo discretizzato, si è rivelato meglio di quanto pensassi.

In [58]:
df['MaxTemp'] = pd.cut(df.MaxTemp, bins = [-100., 10., 20., 34.,np.inf], labels=[0,1,2,3])
temp = df.groupby(['MaxTemp', 'RainTomorrow'])[['RainToday']].count()
temp = temp.rename({'RainToday':'count'}, axis=1)
temp['prob'] = temp['count']/df.groupby('MaxTemp').count()['RainTomorrow']
temp 
Out[58]:
count prob
MaxTemp RainTomorrow
0 0 1168 0.586935
1 822 0.413065
1 0 34990 0.703684
1 14734 0.296316
2 0 64825 0.810333
1 15173 0.189667
3 0 9333 0.890468
1 1148 0.109532

Non piove se la temperatura massima è tra [34,+inf) (90%), dopo i 20° comunque, la probabilità che non piova è circa intorno all'80%.

In [59]:
df['MinTemp'] = pd.cut(df.MinTemp, bins = [-10., 5., 20., np.inf], labels=[0,1,2])
temp = df.groupby(['MinTemp', 'RainTomorrow'])[['RainToday']].count()
temp = temp.rename({'RainToday':'count'}, axis=1)
temp['prob'] = temp['count']/df.groupby('MinTemp').count()['RainTomorrow']
temp 
Out[59]:
count prob
MinTemp RainTomorrow
0 0 16586 0.856848
1 2771 0.143152
1 0 81478 0.773117
1 23911 0.226883
2 0 12252 0.702241
1 5195 0.297759

Non piove se la temperatura minima è tra [-10,5], 85% di probabilità.

La temperatura non sembra molto influente sulla pioggia, ma può essere utile per gli altri attributi. Per verificare che gli attributi scelti fin'ora vanno bene, utilizzo il random forest, per capire se ognuno di essi è fondamentale.

In [60]:
from sklearn.ensemble import RandomForestClassifier

rnd_clf = RandomForestClassifier(n_estimators=500, random_state=42)
df_train=df.copy()
df_train=df_train.drop('RainTomorrow',axis=1)

label=df['RainTomorrow']
rnd_clf.fit(df_train,label)
    
nameCol=df_train.columns
fig, ax = plt.subplots(1,1)
fig.set_size_inches(9,9)
ax.plot(rnd_clf.feature_importances_,'b-')

ax.set_xticks([i for i in range(len(nameCol))])
xlabels = ax.set_xticklabels(nameCol,rotation=30, fontsize=12)
_=ax.set_title('Importanza degli attributi')
In [61]:
for name, score in zip(nameCol, rnd_clf.feature_importances_):
    print(name, score)
MinTemp 0.03654128581577789
MaxTemp 0.04796711356932938
Rainfall 0.04505249411604063
WindGustDir 0.07706161015934182
WindGustSpeed 0.10868055018171013
Humidity 0.3214894891211597
Pressure 0.08812526434186503
Cloud 0.17053981092630546
RainToday 0.10454238176847007

Tutti gli attributi hanno egual importanza, considerando che lo scarto tra di loro è minimo. Fatta eccezione per l'attributo Cloud, che è leggermente più alto. Ma la vera eccezzione è l'attributo Humidity, il quale è significativamente più alto, e quindi migliore, degli altri. Attributo fondamentale.


Dataset finale

Dopo aver effettuato tutte le modifiche agli attributi e dopo esserci accertati dell'effettiva importanza degli attributi rimanenti, abbiamo ottenuto la versione finale, che verrà utilizzata durante la classificazione, del nostro dataset.

In [62]:
df
Out[62]:
MinTemp MaxTemp Rainfall WindGustDir WindGustSpeed Humidity Pressure Cloud RainToday RainTomorrow
0 1 2 0 3.0 3 1 1 1.0 0 0
1 1 2 0 0.0 3 1 1 0.0 0 0
2 1 2 0 2.0 3 1 1 1.0 0 0
3 1 2 0 0.0 1 1 2 0.0 0 0
4 1 2 0 3.0 3 2 1 7.0 0 0
5 1 2 0 0.0 3 1 1 0.0 0 0
6 1 2 0 3.0 3 1 1 0.0 0 0
7 1 2 0 3.0 1 1 2 0.0 0 0
8 1 2 0 0.0 4 0 1 0.0 0 1
9 1 2 0 3.0 1 1 1 0.0 1 0
10 1 2 0 0.0 1 1 2 0.0 0 1
11 1 2 0 0.0 1 4 1 8.0 1 1
12 1 1 1 3.0 4 4 0 8.0 1 1
13 1 2 0 2.0 3 2 1 5.0 1 0
14 1 2 0 0.0 3 1 2 0.0 0 0
15 1 2 0 0.0 1 3 2 2.0 0 1
16 1 2 1 3.0 4 3 1 2.0 1 1
17 1 2 1 1.0 3 1 1 1.0 1 0
18 1 2 0 1.0 1 1 2 0.0 0 0
19 1 2 0 2.0 1 1 2 0.0 0 0
20 1 2 0 0.0 3 1 2 0.0 0 0
21 2 2 0 0.0 3 1 1 0.0 0 0
22 1 2 0 0.0 1 1 1 1.0 0 0
23 1 2 0 3.0 3 1 2 0.0 0 0
24 1 2 0 2.0 1 1 1 0.0 0 0
25 1 2 0 2.0 3 1 1 0.0 0 0
26 2 2 0 0.0 3 1 1 0.0 0 0
27 1 2 0 0.0 3 1 1 0.0 0 1
28 1 2 0 0.0 3 3 1 8.0 1 0
29 1 2 0 3.0 2 1 1 0.0 0 0
... ... ... ... ... ... ... ... ... ... ...
142163 1 2 0 0.0 2 1 2 6.0 0 0
142164 1 2 0 3.0 1 0 2 0.0 0 0
142165 1 2 0 1.0 1 1 3 0.0 0 0
142166 1 2 0 1.0 2 1 3 8.0 0 0
142167 1 2 0 1.0 1 1 3 4.0 0 0
142168 1 2 0 1.0 3 1 3 0.0 0 0
142169 1 1 0 1.0 3 1 3 0.0 0 0
142170 0 1 0 1.0 2 1 3 0.0 0 0
142171 0 1 0 0.0 2 1 3 0.0 0 0
142172 0 1 0 1.0 1 1 3 0.0 0 0
142173 0 2 0 1.0 2 0 3 0.0 0 0
142174 0 2 0 1.0 3 0 3 0.0 0 0
142175 0 2 0 1.0 3 1 3 0.0 0 0
142176 0 1 0 1.0 2 1 3 0.0 0 0
142177 0 1 0 1.0 3 1 3 0.0 0 0
142178 0 1 0 1.0 3 1 3 0.0 0 0
142179 0 1 0 1.0 1 1 3 0.0 0 0
142180 1 1 0 1.0 3 1 3 0.0 0 0
142181 0 2 0 0.0 1 1 3 0.0 0 0
142182 0 2 0 1.0 1 1 3 0.0 0 0
142183 0 2 0 2.0 0 1 3 0.0 0 0
142184 1 2 0 1.0 1 1 3 0.0 0 0
142185 1 2 0 1.0 1 1 3 0.0 0 0
142186 1 2 0 1.0 3 1 3 5.0 0 0
142187 1 2 0 1.0 1 1 3 0.0 0 0
142188 0 2 0 1.0 1 1 3 0.0 0 0
142189 0 2 0 1.0 1 1 3 0.0 0 0
142190 0 2 0 0.0 1 1 3 0.0 0 0
142191 1 2 0 0.0 2 1 2 0.0 0 0
142192 1 2 0 1.0 1 1 2 2.0 0 0

142193 rows × 10 columns

Controlliamo anche la nuova distribuzione degli attributi.

In [63]:
for col in df.columns :
    df[col]=df[col].values.astype('int64')
df.hist(figsize=[15,15])
Out[63]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7fde920633c8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde987e3080>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde98301eb8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7fde987800b8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde90e12358>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde90e12390>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7fde90dfc048>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde90e2b6d8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde90e36d68>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7fde91443438>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde9144fa90>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde91459160>]],
      dtype=object)

Una volta ottenuti i nostri attributi di riferimento, possiamo considerare concluso il preprocessing. L'ultimo passo è quello di dividere il test set dal train set, mantenendo in percentuale la stessa distribuzione di valori, considerando l'attributo sul quale effettueremo la classificazione, ovvero RainTomorrow. Con scikitleran divido il dataset in train e test e controllo se la divisione rispecchia i parametri citati qui sopra.

In [64]:
from sklearn.model_selection import train_test_split

training_set, test_set = train_test_split(df, test_size=0.2, random_state=42)
In [65]:
training_set["RainTomorrow"].hist()
Out[65]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde913dac18>
In [66]:
test_set["RainTomorrow"].hist()
Out[66]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde922e6e80>

Split perfetto! Adesso creo le label

In [67]:
training_label=training_set['RainTomorrow'].copy()
training_set=training_set.drop("RainTomorrow",axis=1)
test_label=test_set['RainTomorrow'].copy()
test_set=test_set.drop('RainTomorrow',axis=1)

Modelli di classificazione

Ora che abbiamo un dataset utile per i nostri scopi, non ci resta che scoprire quale modello di classificazione riesce a predire correttamente, dato un record, se il giorno dopo pioverà oppure no.

2.1 Clustering (Extra)

Questa è una sezione extra, non era richiesto e non era necessiario effettuare del clustering. Però, per completezza abbiamo deciso di utilizzare anche questo stumento.

Si è provato a fare una analisi per località, l'obbiettivo era quello di trovare zone geografiche che avessero una piovosità simile, in realtà non si è ottenuto il risultato richiesto, probabilmente perchè i dati relativi a data e località sono abbastanza incompleti, come già mostrato nella fase di preprocessing.

Dunque il clustering viene fatto sui dati ottenuti nella fase del preprocessing.

In [68]:
# clus lo utilizzo per il clustering
clus = training_set.copy()

Il primo algoritmo utilizzato per il clustering è il DBSCAN, si è pensato che, parlando di dati spazio-temporali, esso potesse modellare abbastanza bene i punti che dovrebbero avere le stesse densità.

In [69]:
from sklearn.cluster import DBSCAN # dbscan

clusters = np.array([])

for e in range(7,11):
    ep = e/10
    dbscan = DBSCAN(eps =  ep, min_samples=150)
    dbscan.fit(clus)
    labels = pd.Series(dbscan.labels_)
    labels = pd.Series(labels.unique())
    labels = np.ones_like(labels)
    clusters = np.append(clusters, labels.sum())

clusters
Out[69]:
array([65., 65., 65.,  2.])

Il numero di classi è ciò che ci aspettiamo. Avendo un dataset molto denso, è normale aspettarsi che per un eps < 1 abbiamo un numero di cluster (65) costante indipendente dal variare di eps, mentre per eps >= 1 succede la stessa cosa, ma il numero di cluster è nettamente più basso (2) proprio perchè i punti sono molto densi.

Vediamo allora cosa succede facendo variare min_samples.

In [70]:
clusters = np.array([])

for e in range(150, 300, 50):
    dbscan = DBSCAN(eps =  0.8, min_samples=e)
    dbscan.fit(clus)
    labels = pd.Series(dbscan.labels_)
    labels = pd.Series(labels.unique())
    labels = np.ones_like(labels)
    clusters = np.append(clusters, labels.sum())

clusters
Out[70]:
array([65., 39., 28.])
In [71]:
plt.figure(figsize=(8, 3.5))
plt.plot(range(3), clusters, "bo-")
plt.xlabel("$Min Samples$", fontsize=14)
plt.ylabel("Cluster Numbers", fontsize=14)
y_min = clusters.min()- 5
y_max = clusters.max()+ 5
plt.axis([-0.5, 2.5, y_min, y_max ])
plt.show()
In [72]:
clusters[1]
Out[72]:
39.0

Scelgo come eps valida 0.8, che mi permette di avere un buon numero di cluster (39), quando min_samples = 200, si è scelto di avere questo numero di cluster in base alla correttezza di Kmeans che viene eseguito dopo, in modo da avere un numero di cluster che ipoteticamente migliorano l'errore il più possibile.

In [73]:
dbscan = DBSCAN(eps = 0.8, min_samples=200)
clusters = dbscan.fit_predict(clus)
In [74]:
cl = pd.Series(clusters)
labels = cl.unique()
labels
Out[74]:
array([-1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
       16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
       33, 34, 35, 36, 37])
In [75]:
cl.hist(bins=40, figsize=[10,10])
Out[75]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde911258d0>

Ci sono 39 cluster. I punti mostrano uno sbilanciamento netto, questo è sicuramente dato dalla forte densità dei punti (come ci si aspetta). Gli altri adattamenti provati mostrano comunque un fortissimo sbilanciamento, ma con un numero di cluster terribilemtne più alto abbassando min_samples, oppure terribilmente più basso portando eps>=1, dunque questo dovrebbe essere un adattamento, tutto sommato, buono.

Passiamo a KMeans e andiamo a valutare i paramentri in input in base alla discesa dell'SSE medio.

In [76]:
from sklearn.cluster import KMeans

inertia = np.array([])

for k in range(36,42):
    kmeans =KMeans(k).fit(clus)
    inertia = np.append(inertia, kmeans.inertia_)

inertia
Out[76]:
array([235715.95459177, 232383.41833808, 231527.15657639, 229588.79173406,
       227677.49857389, 224798.30124661])
In [175]:
plt.figure(figsize=(8, 3.5))
plt.plot(range(36, 42), inertia, "bo-")
plt.xlabel("$k$", fontsize=14)
plt.ylabel("Inertia", fontsize=14)
y_max = inertia.max() + 10000
y_min = inertia.min() - 10000
plt.axis([35.5, 41.5, y_min, y_max])
plt.show()

L'algoritmo con valori di K più grandi fa fatica a terminare, quindi mi sono mantenuto su un valore di cluster che vanno da 45 a 51, dopo diverse run ho notato come a k=49 l'SSE medio mostra sempre un buon abbassamento.

Anche se comunque l'errore è alto, non possiamo fare di molto meglio e quindi manteniamo k=49.

In [78]:
kmeans = KMeans(40).fit(clus)
centroidi = kmeans.cluster_centers_
In [79]:
def plot_centroids(centroids, weights=None, circle_color='w', cross_color='k'):
    if weights is not None:
        centroids = centroids[weights > weights.max() / 10]
    plt.scatter(centroids[:, 0], centroids[:, 1],
                marker='o', s=30, linewidths=8,
                color=circle_color, zorder=10, alpha=0.9)
    plt.scatter(centroids[:, 0], centroids[:, 1],
                marker='x', s=50, linewidths=50,
                color=cross_color, zorder=11, alpha=1)
In [80]:
plot_centroids(centroidi)
In [81]:
kmeans = kmeans.predict(clus)
kmeans
Out[81]:
array([11, 11, 36, ...,  8, 30, 37], dtype=int32)
In [82]:
cl = pd.Series(kmeans)
cl.unique()
Out[82]:
array([11, 36, 13, 19, 39,  4, 16, 31, 29, 21,  7, 28, 22, 24, 27,  8,  1,
       38, 25,  0,  3, 10, 35, 33,  2, 12, 30, 34,  9, 15, 23, 26, 17, 18,
        5, 32, 20, 37,  6, 14])
In [83]:
cl.hist(bins=40, figsize=[10,10])
Out[83]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde90a4efd0>

Sicuramente i cluster sono bilanciati utilizzando kmeans, a volte qualche cluster è vuoto, segno che i dati sono molto densi.

Non sembrano esserci grandi risultati dalla creazione dei cluster (sia con DBSCAN, che con KMeans), tuttavia proveremo questi adattamenti con alcuni algoritmi di classificazione per valutare se effettivamnete non abbiamo miglioramenti.


2.2 Vari modelli di classificazione

2.2.1 Training e Performance dei modelli

Durante questa fase proviamo ad addestrare più modelli per la classificazione. Calcoleremo per ogni modello le performance in termini di Acccuracy, Precision, Recall e F-measure.

In [84]:
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score

def compute_performance(estimators, X, y, scoring_funs, cross_val=True):
    '''
    Compute every score function for each estimator
    
    Parameters:
    ----------
        estimators : a list of predictors
        X : data used for computed the corss_validation_test
        y : target values
        scoring_fun : list of performances measures
    
    Returns:
    --------
        A dictionary containing the score of each estimator
    '''
    score_dict = {}
    for e in estimators:
        score_dict[e.__class__.__name__] = {}
        if not cross_val:
            y_pred = e.predict(X)

        for f in scoring_funs: 
            if cross_val:
                score = cross_val_score(e, X ,y, cv=10 ,scoring=f).mean()
                name = f
            else:
                _f, name = f
                score = _f(y_pred, y)
            score_dict[e.__class__.__name__][name] = score
    
    return score_dict

Il primo classificatore che andiamo ad addestrare è l'albero decisionale utilizzando la metrica del GinIndex.

In [85]:
from sklearn.tree import DecisionTreeClassifier

DTree_clf = DecisionTreeClassifier().fit(training_set, training_label)
In [86]:
nameCol=training_set.columns
fig, ax = plt.subplots(1,1)
fig.set_size_inches(9,9)
ax.plot(DTree_clf.feature_importances_,'b-')

ax.set_xticks([i for i in range(len(nameCol))])
xlabels = ax.set_xticklabels(nameCol,rotation=30, fontsize=12)
_=ax.set_title('Importanza degli attributi')

Confermata l'importanza degli attributi trovata in random forest.

In [87]:
impurità = pd.Series(DTree_clf.tree_.impurity)
print("Frazione di nodi puri: %.2f %%\n" %((1 - impurità[impurità > 0.].count()/impurità.count())*100))
Frazione di nodi puri: 26.34 %

Poco più del 26 % dei nodi dell'albero sono puri.

In [88]:
compute_performance([DTree_clf], training_set, training_label, scoring_funs=['accuracy', 'recall', 'precision', 'f1'])
Out[88]:
{'DecisionTreeClassifier': {'accuracy': 0.8241908442693229,
  'recall': 0.42876751321475715,
  'precision': 0.6696423277996935,
  'f1': 0.5230023782422297}}
In [89]:
test_score = DTree_clf.score(test_set,test_label)
training_score = DTree_clf.score(training_set,training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.866 ,  Test Score : 0.821 

Misurando lo score ottenuto sul training set e sul test set, ci rendiamo conto di come il modello si adatti bene ad entrambi, per questo motivo supponiamo che non ci sia overfitting.

Ora proveremo ad applicare il modello clusterizzando i dati con l'algoritmo KMeans.

In [90]:
pipeline = Pipeline([
    ("kmeans", KMeans(n_clusters=49)),
    ("Decision_tree", DecisionTreeClassifier()),
])
pipeline.fit(training_set, training_label)
Out[90]:
Pipeline(memory=None,
         steps=[('kmeans',
                 KMeans(algorithm='auto', copy_x=True, init='k-means++',
                        max_iter=300, n_clusters=49, n_init=10, n_jobs=None,
                        precompute_distances='auto', random_state=None,
                        tol=0.0001, verbose=0)),
                ('Decision_tree',
                 DecisionTreeClassifier(class_weight=None, criterion='gini',
                                        max_depth=None, max_features=None,
                                        max_leaf_nodes=None,
                                        min_impurity_decrease=0.0,
                                        min_impurity_split=None,
                                        min_samples_leaf=1, min_samples_split=2,
                                        min_weight_fraction_leaf=0.0,
                                        presort=False, random_state=None,
                                        splitter='best'))],
         verbose=False)
In [91]:
test_score = pipeline.score(test_set,test_label)
training_score = pipeline.score(training_set,training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.866 ,  Test Score : 0.820 
In [92]:
DTree_clf = pipeline.named_steps.Decision_tree
compute_performance([DTree_clf], training_set, training_label, scoring_funs=['accuracy', 'recall', 'precision', 'f1'])
Out[92]:
{'DecisionTreeClassifier': {'accuracy': 0.8241381008800959,
  'recall': 0.4289242223054487,
  'precision': 0.6693819813228865,
  'f1': 0.5227543862332958}}

Non ci sono varianzioni di performance rispetto al modello precedente, dunque la clusterizzazione non ha effetto.

Proviamo adesso ad untilizzare l'entropia invece del GiniIndex.

In [93]:
DTree_clf = DecisionTreeClassifier(criterion='entropy').fit(training_set, training_label)
In [94]:
test_score = DTree_clf.score(test_set,test_label)
training_score = DTree_clf.score(training_set,training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.866 ,  Test Score : 0.821 
In [95]:
compute_performance([DTree_clf], training_set, training_label, scoring_funs=['accuracy', 'recall', 'precision', 'f1'])
Out[95]:
{'DecisionTreeClassifier': {'accuracy': 0.8243051292102794,
  'recall': 0.4288458217503875,
  'precision': 0.6698516747272515,
  'f1': 0.5229228959435287}}
In [96]:
impurità = pd.Series(DTree_clf.tree_.impurity)
print("Frazione di nodi puri: %.2f %%\n" %((1 - impurità[impurità > 0.].count()/impurità.count())*100))
Frazione di nodi puri: 26.09 %

Anche il numero di nodi puri è similare.

Il classificatore che utilizzaimo adesso implementa modelli lineari regolarizzati con apprendimento discendente del gradiente stocastico. Il modello è della familgia dei classificatori Support Vector Machines.

In [97]:
from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(max_iter=1000, tol=-np.infty)
sgd_clf.fit(training_set, training_label)
compute_performance([sgd_clf], training_set, training_label, ['accuracy', 'precision', 'recall', 'f1'])
Out[97]:
{'SGDClassifier': {'accuracy': 0.8310916309760529,
  'precision': 0.7206828628010927,
  'recall': 0.3913290703798348,
  'f1': 0.5180255137626325}}

Miglioriamo di molto sulla precision e scendiamo di poco sulla recall.

In [98]:
coef = pd.DataFrame({'Frequenza Coefficenti SGDC':sgd_clf.coef_[0]})
coef.hist()
Out[98]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7fde8bd06438>]],
      dtype=object)
In [99]:
test_score = sgd_clf.score(test_set,test_label)
training_score = sgd_clf.score(training_set,training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.831 ,  Test Score : 0.828 

Anche qua non sembra esserci overfitting, ma c'è un peggioramento di score rispetto all'albero decisionale.

Proviamo ad addestrare un classificatore Bayesiano.

In [100]:
from sklearn.naive_bayes import GaussianNB
nb_clf = GaussianNB()
nb_clf = nb_clf.fit(training_set, training_label)
compute_performance([nb_clf], training_set, training_label, ['accuracy', 'precision', 'recall', 'f1'])
Out[100]:
{'GaussianNB': {'accuracy': 0.8102219921105374,
  'precision': 0.5974844526005442,
  'recall': 0.47384034812791065,
  'f1': 0.5284890465341362}}

Risultati non molto buoni dal punto di vista della precision (scendiamo sia rispetto al DTree che al SGD), ma milgioriamo di qualche punto rispetto alla recall.

In [101]:
test_score = nb_clf.score(test_set,test_label)
training_score = nb_clf.score(training_set,training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.810 ,  Test Score : 0.805 

Lo score somiglia di più a quello ottenuto con SGD che a quello del Dtree, la differenza tra training set e test set questa volta è minima.

Prima di Addestrare il classificatore basato sui KNN, si è applicata una fase pre addestramento volta a cercare il miglior adattamento in termini di numero di knn.

In [102]:
from sklearn.neighbors import KNeighborsClassifier
scores = []

for k in range(10, 100, 20):
    knn_clf = KNeighborsClassifier(n_neighbors=k)
    knn_clf.fit(training_set, training_label)
    scores.append(cross_val_score(knn_clf, training_set, training_label, cv=10 ,scoring='recall').mean())
scores
Out[102]:
[0.40769877500666374,
 0.4131035209394816,
 0.40734647861575785,
 0.4024908586363763,
 0.39971024614891015]

Si è voluto misurare la Recall, essendo esso il parametro che più si dovrebbe migliorare, tuttavia cambiare il numero di KNN in input non dà grandi vantaggi in questo senso. si è scelto KNN=60.

In [103]:
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier(n_neighbors=60)
knn_clf.fit(training_set, training_label)
compute_performance([knn_clf], training_set, training_label, ['accuracy', 'precision', 'recall', 'f1'])
Out[103]:
{'KNeighborsClassifier': {'accuracy': 0.8362430884667841,
  'precision': 0.7481317438967235,
  'recall': 0.40789489908689125,
  'f1': 0.5279130361217678}}
In [104]:
test_score = knn_clf.score(test_set,test_label)
training_score = knn_clf.score(training_set,training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.839 ,  Test Score : 0.834 

Lo score è abbastanza alto (più dei due precedenti, ma sempre meno del DTree) con una differenza tra training set e test set, minima.

Proviamo a Clusterizzare con DBSCAN prima di addestrare il modello.

In [105]:
dbscan = DBSCAN(eps= 0.8, min_samples=200)
dbscan = dbscan.fit(training_set)
knn = KNeighborsClassifier(n_neighbors=60)
knn.fit(dbscan.components_, dbscan.labels_[dbscan.core_sample_indices_])
compute_performance([knn], training_set, training_label, ['accuracy', 'precision', 'recall', 'f1'])
Out[105]:
{'KNeighborsClassifier': {'accuracy': 0.8362430884667841,
  'precision': 0.7481317438967235,
  'recall': 0.40789489908689125,
  'f1': 0.5279130361217678}}
In [106]:
test_score = knn.score(test_set,test_label)
training_score = knn.score(training_set,training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.026 ,  Test Score : 0.025 

In temrini di Accuracy, Precision, Recall e F-measure non ci sono cambiamenti, oltretutto lo score degrada moltissimo, sicuramente non un buon risultato.

Proviamo ad addestrare il classico classificatore Support Vector.

In [107]:
from sklearn.svm import LinearSVC
svc_clf = LinearSVC(max_iter= 500)
svc_clf.fit(training_set, training_label)
compute_performance([svc_clf], training_set, training_label, ['accuracy', 'precision', 'recall', 'f1'])
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
Out[107]:
{'LinearSVC': {'accuracy': 0.831074026144835,
  'precision': 0.7134209043881647,
  'recall': 0.42418374930717045,
  'f1': 0.5234338528579092}}
In [108]:
test_score = svc_clf.score(test_set,test_label)
training_score = svc_clf.score(training_set,training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.831 ,  Test Score : 0.827 

Risultati comunque abbastanza buoni, meglio delgi altri classificatori in generale.


2.2.2 Ensamble

Cerchiamo di migliorare i risultati andando ad utilizzare gli ensemble. Nello specifico verranno addestrati: RandomForest, Voting e Bagging.

In [109]:
from sklearn.ensemble import RandomForestClassifier
rnd_clf = RandomForestClassifier(n_estimators=500)
rnd_clf.fit(training_set,training_label)
Out[109]:
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
                       max_depth=None, max_features='auto', max_leaf_nodes=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=2,
                       min_weight_fraction_leaf=0.0, n_estimators=500,
                       n_jobs=None, oob_score=False, random_state=None,
                       verbose=0, warm_start=False)
In [110]:
compute_performance([rnd_clf], training_set, training_label, scoring_funs=['accuracy', 'recall', 'precision'])
Out[110]:
{'RandomForestClassifier': {'accuracy': 0.8279972815838276,
  'recall': 0.45970409311630245,
  'precision': 0.6691475437448089}}

Le statistiche del RandomForest sono esattamente uguali a quelle del semplice DTree.

Per il Voting classifier, si sono utilizzati i tre classificatori visti prima: SVC, NaiveBayes e KNN, e in più si è aggiunto anche il Random Forest.

In [111]:
from sklearn.ensemble import VotingClassifier

nb_clf = GaussianNB()
svc_clf = LinearSVC(max_iter= 500)
rnd_clf = RandomForestClassifier(n_estimators=500)
knn_clf = KNeighborsClassifier(n_neighbors=60)

voting_clf = VotingClassifier(
    estimators=[('nb', nb_clf), ('svc', svc_clf), ('rf', rnd_clf), ('knn', knn_clf)],
    voting='hard')
voting_clf.fit(training_set,training_label)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
Out[111]:
VotingClassifier(estimators=[('nb',
                              GaussianNB(priors=None, var_smoothing=1e-09)),
                             ('svc',
                              LinearSVC(C=1.0, class_weight=None, dual=True,
                                        fit_intercept=True, intercept_scaling=1,
                                        loss='squared_hinge', max_iter=500,
                                        multi_class='ovr', penalty='l2',
                                        random_state=None, tol=0.0001,
                                        verbose=0)),
                             ('rf',
                              RandomForestClassifier(bootstrap=True,
                                                     class_weight=None,
                                                     criterion='g...
                                                     min_samples_split=2,
                                                     min_weight_fraction_leaf=0.0,
                                                     n_estimators=500,
                                                     n_jobs=None,
                                                     oob_score=False,
                                                     random_state=None,
                                                     verbose=0,
                                                     warm_start=False)),
                             ('knn',
                              KNeighborsClassifier(algorithm='auto',
                                                   leaf_size=30,
                                                   metric='minkowski',
                                                   metric_params=None,
                                                   n_jobs=None, n_neighbors=60,
                                                   p=2, weights='uniform'))],
                 flatten_transform=True, n_jobs=None, voting='hard',
                 weights=None)
In [112]:
compute_performance([voting_clf], training_set, training_label, scoring_funs=['accuracy', 'recall', 'precision'])
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
Out[112]:
{'VotingClassifier': {'accuracy': 0.834142093887934,
  'recall': 0.39046727773703366,
  'precision': 0.7504657920819782}}

Il migliore in termini di precision, ma perdiamo troppo sulla recall.

Infine il BaggingClassifier viene utilizzato con il Dtree, si è provato anche con altri modelli, ma non si sono ottenuti miglioramenti.

In [113]:
from sklearn.ensemble import BaggingClassifier

bag_clf = BaggingClassifier(
    DecisionTreeClassifier(), n_estimators=500,
    max_samples=100, bootstrap=True)
bag_clf.fit(training_set, training_label)
Out[113]:
BaggingClassifier(base_estimator=DecisionTreeClassifier(class_weight=None,
                                                        criterion='gini',
                                                        max_depth=None,
                                                        max_features=None,
                                                        max_leaf_nodes=None,
                                                        min_impurity_decrease=0.0,
                                                        min_impurity_split=None,
                                                        min_samples_leaf=1,
                                                        min_samples_split=2,
                                                        min_weight_fraction_leaf=0.0,
                                                        presort=False,
                                                        random_state=None,
                                                        splitter='best'),
                  bootstrap=True, bootstrap_features=False, max_features=1.0,
                  max_samples=100, n_estimators=500, n_jobs=None,
                  oob_score=False, random_state=None, verbose=0,
                  warm_start=False)
In [114]:
compute_performance([bag_clf], training_set, training_label, scoring_funs=['accuracy', 'recall', 'precision'])
Out[114]:
{'BaggingClassifier': {'accuracy': 0.8345376171354809,
  'recall': 0.4391443450532348,
  'precision': 0.7175880801107853}}

Il Bagging è quello che ha dato il miglior risultato, ripettando le caratteristiche dei dati.

Il problema principale dei classificatori sono sulle recall, molto basso come valore. Arrivati a aquesto punto si suppone che non si possa fare molto meglio di così.

Adesso proviamo a calcolare matrice di confusione e curva ROC di quest'ultimo modello.

In [115]:
from sklearn.metrics import confusion_matrix
from sklearn.utils.multiclass import unique_labels

def plot_confusion_matrix(y_true, y_pred, classes, 
                          classes_value,
                          normalize=False,
                          title=None,
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    if not title:
        if normalize:
            title = 'Normalized confusion matrix'
        else:
            title = 'Confusion matrix, without normalization'

    # Compute confusion matrix
    cm = confusion_matrix(y_true, y_pred)
    # Only use the labels that appear in the data
    classes = classes[unique_labels(y_true, y_pred)]
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
        print("Normalized confusion matrix")
    else:
        print('Confusion matrix, without normalization')

    print(cm)

    fig, ax = plt.subplots()
    im = ax.imshow(cm, interpolation='nearest', cmap=cmap)
    ax.figure.colorbar(im, ax=ax)
    # We want to show all ticks...
    ax.set(xticks=np.arange(cm.shape[1]),
           yticks=np.arange(cm.shape[0]),
           # ... and label them with the respective list entries
           xticklabels=classes_value, yticklabels=classes_value,
           title=title,
           ylabel='True label',
           xlabel='Predicted label')

    # Rotate the tick labels and set their alignment.
    plt.setp(ax.get_xticklabels(), rotation=45, ha="right",
             rotation_mode="anchor")

    # Loop over data dimensions and create text annotations.
    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i in range(cm.shape[0]):
        for j in range(cm.shape[1]):
            ax.text(j, i, format(cm[i, j], fmt),
                    ha="center", va="center",
                    color="white" if cm[i, j] > thresh else "black")
    fig.tight_layout()
    return ax
In [116]:
classes_value = ["Rain Tomorrow: NO", "RainTomorrow: YES"]
test_pred = bag_clf.predict(test_set)
plot_confusion_matrix(test_label, test_pred, classes=df.RainTomorrow, classes_value=classes_value)
Confusion matrix, without normalization
[[20851  1247]
 [ 3543  2798]]
Out[116]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde911369e8>
In [117]:
plot_confusion_matrix(test_label, test_pred, classes=df.RainTomorrow, classes_value=classes_value, normalize=True)
Normalized confusion matrix
[[0.94356955 0.05643045]
 [0.55874468 0.44125532]]
Out[117]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fdea81f3588>
In [118]:
from sklearn.metrics import roc_curve

def plot_roc_curve(fpr, tpr, label=None):
    plt.plot(fpr, tpr, linewidth=2, label=label)
    plt.plot([0, 1], [0, 1], 'k--')
    plt.axis([0, 1, 0, 1])
    plt.xlabel('False Positive Rate', fontsize=16)
    plt.ylabel('True Positive Rate', fontsize=16)
In [119]:
from sklearn.model_selection import cross_val_predict

train_scores = cross_val_predict(bag_clf, training_set, training_label, cv=3)
fpr, tpr, thresholds = roc_curve(training_label, train_scores)
In [120]:
plt.figure(figsize=(8, 6))
plot_roc_curve(fpr, tpr)
plt.show()

2.2.3 Post-processing

Le prestazioni non sono molto soddisfacenti, per non parlare della precision e, soprattutto, la recall che sono molto basse.Quindi abbiamo decisio di effettuare delle operazioni di post-processing. Per migliorare questi fattori abbiamo provato a tornare indetro al dataset originale, e a compiere delle scelti differenti er quanto riguarda gli attributi. Nonostante le nostre innumerevoli scelte non si sono riscontrati dei miglioramenti, anzi in molti casi peggioravano le prestazioni. L'ultima nostra possibilità per migliorare le prestuazioni, riguradava il bilanciamento del dataset. Perchè sii è notato che nel dataset origninale c'è un forte sbilanciamento delle classi (80% di No e 20% di Si), probabilmente a causa di questo la classe 'Yes' viene vista come classe rara anche se non dovrebbe. Dunque proviamo a creare un dataset che sia più bilanciato (40 % di si) e vediamo se riusciamo ad ottenere buone performance

Abbiamo creato una funzione, che a partire dal dataset originale, crea un dataset bilanciato secondo le nostre esigenze.

In [121]:
from sklearn.utils import shuffle

def creaDatasetBilanciato(df, class_coloumn, num_elem=5000,bilancio=0.35):
    df_yes = df[df[class_coloumn] == 1]
    df_no = df[df[class_coloumn] == 0]
    sample_yes = int(num_elem*bilancio)
    sample_no = int(num_elem*(1-bilancio))
    df_yes = df_yes.sample(n=sample_yes)
    df_no = df_no.sample(n=sample_no)
    df_new = pd.concat([df_yes, df_no])
    df_new = shuffle(df_new, random_state=1)
    return df_new
In [122]:
df_ = creaDatasetBilanciato(df, num_elem=90000, class_coloumn='RainTomorrow')
df_.hist(figsize=[15,15])
Out[122]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7fde92230668>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde90abd160>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde90ad32b0>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7fde8bf34940>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde8bf03fd0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde8bf15048>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7fde90ff8d30>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde90fdf400>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde9079aa90>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x7fde906e0160>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde906c87f0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x7fde9069fe80>]],
      dtype=object)

La distribuzione degli attributi è uguale a quella del dataset originale, quindi la funzione è fatta bene.

In [123]:
_training_set, _test_set = train_test_split(df_, test_size=0.2, random_state=42)
_training_set["RainTomorrow"].hist()
Out[123]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde8be53be0>
In [124]:
_test_set["RainTomorrow"].hist()
Out[124]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde90bfb860>
In [125]:
_training_label=_training_set['RainTomorrow'].copy()
_training_set=_training_set.drop("RainTomorrow",axis=1)
_test_label=_test_set['RainTomorrow'].copy()
_test_set=_test_set.drop('RainTomorrow',axis=1)
In [126]:
DTree_clf = DecisionTreeClassifier().fit(_training_set, _training_label)
In [127]:
compute_performance([DTree_clf], _training_set, _training_label, scoring_funs=['accuracy', 'recall', 'precision', 'f1'])
Out[127]:
{'DecisionTreeClassifier': {'accuracy': 0.7655107018258023,
  'recall': 0.5625400671979437,
  'precision': 0.7080050718627656,
  'f1': 0.6271336224274753}}
In [128]:
impurità = pd.Series(DTree_clf.tree_.impurity)
print("Frazione di nodi puri: %.2f %%\n" %((1 - impurità[impurità > 0.].count()/impurità.count())*100))
Frazione di nodi puri: 27.04 %

In [129]:
test_score = DTree_clf.score(_test_set,_test_label)
training_score = DTree_clf.score(_training_set,_training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.831 ,  Test Score : 0.769 

Si abbassa di molto l'accuracy (circa del 12-13%), però c'è un netto milgioramento per quanto riguarda la recall che passa dal 40% al 56% perdendo solo il 3% sulla precision. Si alza anche la percentuale di nodi puri.

Tuttavia lo score si abbassa (così coma l'accuracy), questo può essere sintomo di un aumento di overfitting, anche se in realtà abbiamo notato che aumentando il numero di record (num_ele) nella creazione del dataset bilanciato si migliora questo aspetto. Tuttavia non possiamo alzare il parametro perchè non abbiamo a disposizione troppi record con etichetta di classe 'YES'.

In [130]:
sgd_clf = SGDClassifier(max_iter=1000, tol=-np.infty)
sgd_clf.fit(_training_set, _training_label)
compute_performance([sgd_clf], _training_set, _training_label, ['accuracy', 'precision', 'recall', 'f1'])
Out[130]:
{'SGDClassifier': {'accuracy': 0.7777190314243534,
  'precision': 0.73283528415711,
  'recall': 0.5815242557692681,
  'f1': 0.6530157058402295}}
In [131]:
test_score = sgd_clf.score(_test_set,_test_label)
training_score = sgd_clf.score(_training_set,_training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.778 ,  Test Score : 0.782 
In [132]:
nb_clf = GaussianNB()
nb_clf = nb_clf.fit(_training_set, _training_label)
compute_performance([nb_clf], _training_set, _training_label, ['accuracy', 'precision', 'recall', 'f1'])
Out[132]:
{'GaussianNB': {'accuracy': 0.7602468437727122,
  'precision': 0.7070869335361627,
  'recall': 0.5394341000984288,
  'f1': 0.6119630388970894}}
In [133]:
test_score = nb_clf.score(_test_set,_test_label)
training_score = nb_clf.score(_training_set,_training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.760 ,  Test Score : 0.767 
In [134]:
knn_clf = KNeighborsClassifier(n_neighbors=60)
knn_clf.fit(_training_set, _training_label)
compute_performance([knn_clf], _training_set, _training_label, ['accuracy', 'precision', 'recall', 'f1'])
Out[134]:
{'KNeighborsClassifier': {'accuracy': 0.7837888173712371,
  'precision': 0.7494554438532626,
  'recall': 0.5754999959171188,
  'f1': 0.6509567470799731}}
In [135]:
test_score = knn_clf.score(_test_set,_test_label)
training_score = knn_clf.score(_training_set,_training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.790 ,  Test Score : 0.789 
In [136]:
svc_clf = LinearSVC(max_iter= 500)
svc_clf.fit(_training_set, _training_label)
compute_performance([svc_clf], _training_set, _training_label, ['accuracy', 'precision', 'recall', 'f1'])
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
Out[136]:
{'LinearSVC': {'accuracy': 0.7782608292788654,
  'precision': 0.7268773576965732,
  'recall': 0.5927394437105727,
  'f1': 0.6525005023874884}}
In [137]:
test_score = svc_clf.score(_test_set,_test_label)
training_score = svc_clf.score(_training_set,_training_label)
print("Training Score : %.3f ," %training_score, " Test Score : %.3f "  %test_score)
Training Score : 0.778 ,  Test Score : 0.782 

Proviamo anche gli ensamble learnig su questo nuovo training set.

In [138]:
rnd_clf = RandomForestClassifier(n_estimators=500)
rnd_clf.fit(_training_set,_training_label)
Out[138]:
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
                       max_depth=None, max_features='auto', max_leaf_nodes=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=2,
                       min_weight_fraction_leaf=0.0, n_estimators=500,
                       n_jobs=None, oob_score=False, random_state=None,
                       verbose=0, warm_start=False)
In [139]:
compute_performance([rnd_clf], _training_set, _training_label, scoring_funs=['accuracy', 'recall', 'precision'])
Out[139]:
{'RandomForestClassifier': {'accuracy': 0.7729413365525807,
  'recall': 0.6060958516042267,
  'precision': 0.7066070137029458}}
In [140]:
nb_clf = GaussianNB()
svc_clf = LinearSVC(max_iter= 500)
rnd_clf = RandomForestClassifier(n_estimators=500)
knn_clf = KNeighborsClassifier(n_neighbors=60)

voting_clf = VotingClassifier(
    estimators=[('nb', nb_clf), ('svc', svc_clf), ('rf', rnd_clf), ('knn', knn_clf)],
    voting='hard')
voting_clf.fit(_training_set,_training_label)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
Out[140]:
VotingClassifier(estimators=[('nb',
                              GaussianNB(priors=None, var_smoothing=1e-09)),
                             ('svc',
                              LinearSVC(C=1.0, class_weight=None, dual=True,
                                        fit_intercept=True, intercept_scaling=1,
                                        loss='squared_hinge', max_iter=500,
                                        multi_class='ovr', penalty='l2',
                                        random_state=None, tol=0.0001,
                                        verbose=0)),
                             ('rf',
                              RandomForestClassifier(bootstrap=True,
                                                     class_weight=None,
                                                     criterion='g...
                                                     min_samples_split=2,
                                                     min_weight_fraction_leaf=0.0,
                                                     n_estimators=500,
                                                     n_jobs=None,
                                                     oob_score=False,
                                                     random_state=None,
                                                     verbose=0,
                                                     warm_start=False)),
                             ('knn',
                              KNeighborsClassifier(algorithm='auto',
                                                   leaf_size=30,
                                                   metric='minkowski',
                                                   metric_params=None,
                                                   n_jobs=None, n_neighbors=60,
                                                   p=2, weights='uniform'))],
                 flatten_transform=True, n_jobs=None, voting='hard',
                 weights=None)
In [141]:
compute_performance([voting_clf], _training_set, _training_label, scoring_funs=['accuracy', 'recall', 'precision'])
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/home/bobo/anaconda3/lib/python3.7/site-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
Out[141]:
{'VotingClassifier': {'accuracy': 0.7818720522989508,
  'recall': 0.5464096241676418,
  'precision': 0.7625175544284291}}
In [142]:
bag_clf = BaggingClassifier(
    DecisionTreeClassifier(), n_estimators=500,
    max_samples=100, bootstrap=True)
bag_clf.fit(_training_set, _training_label)
Out[142]:
BaggingClassifier(base_estimator=DecisionTreeClassifier(class_weight=None,
                                                        criterion='gini',
                                                        max_depth=None,
                                                        max_features=None,
                                                        max_leaf_nodes=None,
                                                        min_impurity_decrease=0.0,
                                                        min_impurity_split=None,
                                                        min_samples_leaf=1,
                                                        min_samples_split=2,
                                                        min_weight_fraction_leaf=0.0,
                                                        presort=False,
                                                        random_state=None,
                                                        splitter='best'),
                  bootstrap=True, bootstrap_features=False, max_features=1.0,
                  max_samples=100, n_estimators=500, n_jobs=None,
                  oob_score=False, random_state=None, verbose=0,
                  warm_start=False)
In [143]:
compute_performance([bag_clf], _training_set, _training_label, scoring_funs=['accuracy', 'recall', 'precision'])
Out[143]:
{'BaggingClassifier': {'accuracy': 0.7836498300885169,
  'recall': 0.5969804894809276,
  'precision': 0.7348497968768298}}
In [144]:
classes_value = ["Rain Tomorrow: NO", "RainTomorrow: YES"]
test_pred = bag_clf.predict(_test_set)
plot_confusion_matrix(_test_label, test_pred, classes=df.RainTomorrow, classes_value=classes_value)
Confusion matrix, without normalization
[[10385  1348]
 [ 2470  3797]]
Out[144]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde90a34048>
In [145]:
plot_confusion_matrix(_test_label, test_pred, classes=df.RainTomorrow, classes_value=classes_value, normalize=True)
Normalized confusion matrix
[[0.88511037 0.11488963]
 [0.39412797 0.60587203]]
Out[145]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fde90ddec50>

Come prevedebile sale di poco la percentuale di FN e scende abbastanza quella dei FP, allo stesso modo scende la percentuale di TP e sale quella di TN

In [146]:
train_scores = cross_val_predict(bag_clf, _training_set, _training_label, cv=3)
fpr, tpr, thresholds = roc_curve(_training_label, train_scores)
In [147]:
plt.figure(figsize=(8, 6))
plot_roc_curve(fpr, tpr)
plt.show()

Il punto di picco si sposta da 0.4 a 0.6, mentre l'andamento resta simile a prima.


2.3 Rete neurale

Anche questa sezione si potrebbe considerare extra dato che non era obbligatoria. Però le reti neurali in genere forniscono degli ottimi risultati come classificatori, quindi abbiamo voluto testare diverse reti per controllare se le performance aumentano.

2.3.1 Training e Performance

La rete neurale presente qui sotto è formata da 3 strati: lo strato di input, intermedio e di output. Ho provato anche delle reti di diversa tipologia, deep leraning ad esempio, però questa è quella che presenta le performance migliori. I primi due strati sono formati da quattro neuroni e la loro funzione di attivazione è la 'relu', è quella che presentava i risultati migliori. Mentre, l'ultimo strato, quello che fornisce i risultati della rete, è formato da due neuroni che rappresentano rispettivamente la classe 'no' e la classe 'yes'. Inoltre grazie alla funzione di attivazione 'softmax' oltre alla classe, restituisce in output la probabilità associatà alla classe. Quindi è come se fornisse una misura che indica la sicurezza che ha la rete nell'assegnare la classe a un record.

In [148]:
from keras.utils import to_categorical

test_label2=test_label.copy()

#one-hot encode labels
training_label=to_categorical(training_label)
test_label=to_categorical(test_label)
Using TensorFlow backend.
In [149]:
import keras 

modello=keras.Sequential()
modello.add(keras.layers.Dense(units=4,activation='relu',input_shape=(9,)))
modello.add(keras.layers.Dense(units=4,activation='relu'))
modello.add(keras.layers.Dense(units=2,activation='softmax'))

epoche=100
learning_rate=0.01
batch=1024

modello.compile(optimizer=keras.optimizers.SGD(learning_rate),
                    loss='categorical_crossentropy',
                    metrics=['accuracy']
                    )

history=modello.fit(training_set,training_label, validation_data=(test_set, test_label),epochs=epoche,batch_size=batch)
WARNING:tensorflow:From /home/bobo/anaconda3/lib/python3.7/site-packages/tensorflow/python/framework/op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
WARNING:tensorflow:From /home/bobo/anaconda3/lib/python3.7/site-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Train on 113754 samples, validate on 28439 samples
Epoch 1/100
113754/113754 [==============================] - 23s 204us/step - loss: 0.6453 - acc: 0.7242 - val_loss: 0.5857 - val_acc: 0.7726
Epoch 2/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.5650 - acc: 0.7749 - val_loss: 0.5486 - val_acc: 0.7771
Epoch 3/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.5400 - acc: 0.7756 - val_loss: 0.5309 - val_acc: 0.7772
Epoch 4/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.5248 - acc: 0.7760 - val_loss: 0.5183 - val_acc: 0.7779
Epoch 5/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.5140 - acc: 0.7775 - val_loss: 0.5096 - val_acc: 0.7794
Epoch 6/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.5066 - acc: 0.7804 - val_loss: 0.5035 - val_acc: 0.7808
Epoch 7/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.5012 - acc: 0.7821 - val_loss: 0.4990 - val_acc: 0.7835
Epoch 8/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4970 - acc: 0.7851 - val_loss: 0.4953 - val_acc: 0.7859
Epoch 9/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4935 - acc: 0.7870 - val_loss: 0.4922 - val_acc: 0.7878
Epoch 10/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4905 - acc: 0.7895 - val_loss: 0.4893 - val_acc: 0.7901
Epoch 11/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4877 - acc: 0.7916 - val_loss: 0.4866 - val_acc: 0.7926
Epoch 12/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4849 - acc: 0.7942 - val_loss: 0.4838 - val_acc: 0.7954
Epoch 13/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4819 - acc: 0.7970 - val_loss: 0.4809 - val_acc: 0.7979
Epoch 14/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4787 - acc: 0.7997 - val_loss: 0.4777 - val_acc: 0.8004
Epoch 15/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4750 - acc: 0.8022 - val_loss: 0.4741 - val_acc: 0.8026
Epoch 16/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4709 - acc: 0.8045 - val_loss: 0.4697 - val_acc: 0.8047
Epoch 17/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4659 - acc: 0.8068 - val_loss: 0.4642 - val_acc: 0.8065
Epoch 18/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4593 - acc: 0.8086 - val_loss: 0.4570 - val_acc: 0.8080
Epoch 19/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4512 - acc: 0.8102 - val_loss: 0.4486 - val_acc: 0.8096
Epoch 20/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4426 - acc: 0.8124 - val_loss: 0.4407 - val_acc: 0.8108
Epoch 21/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4350 - acc: 0.8144 - val_loss: 0.4341 - val_acc: 0.8130
Epoch 22/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4288 - acc: 0.8162 - val_loss: 0.4289 - val_acc: 0.8164
Epoch 23/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4238 - acc: 0.8179 - val_loss: 0.4248 - val_acc: 0.8168
Epoch 24/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4196 - acc: 0.8193 - val_loss: 0.4210 - val_acc: 0.8181
Epoch 25/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4159 - acc: 0.8211 - val_loss: 0.4179 - val_acc: 0.8198
Epoch 26/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4127 - acc: 0.8229 - val_loss: 0.4154 - val_acc: 0.8205
Epoch 27/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4100 - acc: 0.8241 - val_loss: 0.4132 - val_acc: 0.8206
Epoch 28/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4077 - acc: 0.8246 - val_loss: 0.4118 - val_acc: 0.8212
Epoch 29/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4058 - acc: 0.8252 - val_loss: 0.4099 - val_acc: 0.8210
Epoch 30/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4042 - acc: 0.8263 - val_loss: 0.4085 - val_acc: 0.8223
Epoch 31/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4028 - acc: 0.8271 - val_loss: 0.4073 - val_acc: 0.8227
Epoch 32/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.4017 - acc: 0.8273 - val_loss: 0.4064 - val_acc: 0.8234
Epoch 33/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.4007 - acc: 0.8278 - val_loss: 0.4055 - val_acc: 0.8229
Epoch 34/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3998 - acc: 0.8277 - val_loss: 0.4050 - val_acc: 0.8228
Epoch 35/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3990 - acc: 0.8282 - val_loss: 0.4042 - val_acc: 0.8234
Epoch 36/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3983 - acc: 0.8285 - val_loss: 0.4036 - val_acc: 0.8244
Epoch 37/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3978 - acc: 0.8289 - val_loss: 0.4038 - val_acc: 0.8233
Epoch 38/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3972 - acc: 0.8292 - val_loss: 0.4027 - val_acc: 0.8240
Epoch 39/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3967 - acc: 0.8294 - val_loss: 0.4024 - val_acc: 0.8236
Epoch 40/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3963 - acc: 0.8299 - val_loss: 0.4023 - val_acc: 0.8234
Epoch 41/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3959 - acc: 0.8296 - val_loss: 0.4014 - val_acc: 0.8241
Epoch 42/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3955 - acc: 0.8298 - val_loss: 0.4014 - val_acc: 0.8238
Epoch 43/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3952 - acc: 0.8298 - val_loss: 0.4007 - val_acc: 0.8247
Epoch 44/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3948 - acc: 0.8302 - val_loss: 0.4004 - val_acc: 0.8248
Epoch 45/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3945 - acc: 0.8300 - val_loss: 0.4003 - val_acc: 0.8244
Epoch 46/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3943 - acc: 0.8302 - val_loss: 0.4000 - val_acc: 0.8248
Epoch 47/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3940 - acc: 0.8301 - val_loss: 0.3999 - val_acc: 0.8248
Epoch 48/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3938 - acc: 0.8304 - val_loss: 0.3994 - val_acc: 0.8251
Epoch 49/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3935 - acc: 0.8304 - val_loss: 0.3991 - val_acc: 0.8248
Epoch 50/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3933 - acc: 0.8305 - val_loss: 0.3989 - val_acc: 0.8249
Epoch 51/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3931 - acc: 0.8306 - val_loss: 0.3987 - val_acc: 0.8248
Epoch 52/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3929 - acc: 0.8305 - val_loss: 0.3986 - val_acc: 0.8250
Epoch 53/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3927 - acc: 0.8305 - val_loss: 0.3983 - val_acc: 0.8254
Epoch 54/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3926 - acc: 0.8306 - val_loss: 0.3981 - val_acc: 0.8251
Epoch 55/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3924 - acc: 0.8306 - val_loss: 0.3980 - val_acc: 0.8250
Epoch 56/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3923 - acc: 0.8307 - val_loss: 0.3979 - val_acc: 0.8249
Epoch 57/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3921 - acc: 0.8303 - val_loss: 0.3977 - val_acc: 0.8254
Epoch 58/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3920 - acc: 0.8305 - val_loss: 0.3976 - val_acc: 0.8258
Epoch 59/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3919 - acc: 0.8306 - val_loss: 0.3977 - val_acc: 0.8251
Epoch 60/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3917 - acc: 0.8307 - val_loss: 0.3972 - val_acc: 0.8258
Epoch 61/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3916 - acc: 0.8308 - val_loss: 0.3979 - val_acc: 0.8245
Epoch 62/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3915 - acc: 0.8308 - val_loss: 0.3975 - val_acc: 0.8248
Epoch 63/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3914 - acc: 0.8306 - val_loss: 0.3969 - val_acc: 0.8253
Epoch 64/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3913 - acc: 0.8308 - val_loss: 0.3971 - val_acc: 0.8254
Epoch 65/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3912 - acc: 0.8309 - val_loss: 0.3968 - val_acc: 0.8255
Epoch 66/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3911 - acc: 0.8309 - val_loss: 0.3966 - val_acc: 0.8252
Epoch 67/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3909 - acc: 0.8307 - val_loss: 0.3966 - val_acc: 0.8256
Epoch 68/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3909 - acc: 0.8309 - val_loss: 0.3963 - val_acc: 0.8260
Epoch 69/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3908 - acc: 0.8311 - val_loss: 0.3962 - val_acc: 0.8262
Epoch 70/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3907 - acc: 0.8310 - val_loss: 0.3961 - val_acc: 0.8257
Epoch 71/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3906 - acc: 0.8312 - val_loss: 0.3961 - val_acc: 0.8256
Epoch 72/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3905 - acc: 0.8312 - val_loss: 0.3961 - val_acc: 0.8257
Epoch 73/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3905 - acc: 0.8312 - val_loss: 0.3959 - val_acc: 0.8258
Epoch 74/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3904 - acc: 0.8313 - val_loss: 0.3963 - val_acc: 0.8255
Epoch 75/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3903 - acc: 0.8310 - val_loss: 0.3959 - val_acc: 0.8259
Epoch 76/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3903 - acc: 0.8311 - val_loss: 0.3958 - val_acc: 0.8258
Epoch 77/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3902 - acc: 0.8311 - val_loss: 0.3956 - val_acc: 0.8260
Epoch 78/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3901 - acc: 0.8312 - val_loss: 0.3959 - val_acc: 0.8260
Epoch 79/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3901 - acc: 0.8312 - val_loss: 0.3955 - val_acc: 0.8259
Epoch 80/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3901 - acc: 0.8312 - val_loss: 0.3955 - val_acc: 0.8262
Epoch 81/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3900 - acc: 0.8310 - val_loss: 0.3953 - val_acc: 0.8262
Epoch 82/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3899 - acc: 0.8312 - val_loss: 0.3953 - val_acc: 0.8262
Epoch 83/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3899 - acc: 0.8312 - val_loss: 0.3962 - val_acc: 0.8247
Epoch 84/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3898 - acc: 0.8315 - val_loss: 0.3952 - val_acc: 0.8259
Epoch 85/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3898 - acc: 0.8314 - val_loss: 0.3953 - val_acc: 0.8264
Epoch 86/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3897 - acc: 0.8314 - val_loss: 0.3951 - val_acc: 0.8264
Epoch 87/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3897 - acc: 0.8314 - val_loss: 0.3961 - val_acc: 0.8253
Epoch 88/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3897 - acc: 0.8313 - val_loss: 0.3951 - val_acc: 0.8266
Epoch 89/100
113754/113754 [==============================] - 0s 3us/step - loss: 0.3896 - acc: 0.8315 - val_loss: 0.3954 - val_acc: 0.8263
Epoch 90/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3896 - acc: 0.8315 - val_loss: 0.3951 - val_acc: 0.8279
Epoch 91/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3896 - acc: 0.8315 - val_loss: 0.3951 - val_acc: 0.8266
Epoch 92/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3895 - acc: 0.8314 - val_loss: 0.3949 - val_acc: 0.8264
Epoch 93/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3895 - acc: 0.8316 - val_loss: 0.3950 - val_acc: 0.8266
Epoch 94/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3895 - acc: 0.8313 - val_loss: 0.3948 - val_acc: 0.8266
Epoch 95/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3894 - acc: 0.8317 - val_loss: 0.3948 - val_acc: 0.8271
Epoch 96/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3894 - acc: 0.8316 - val_loss: 0.3947 - val_acc: 0.8271
Epoch 97/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3894 - acc: 0.8317 - val_loss: 0.3954 - val_acc: 0.8257
Epoch 98/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3893 - acc: 0.8318 - val_loss: 0.3947 - val_acc: 0.8267
Epoch 99/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3893 - acc: 0.8318 - val_loss: 0.3946 - val_acc: 0.8273
Epoch 100/100
113754/113754 [==============================] - 0s 2us/step - loss: 0.3893 - acc: 0.8317 - val_loss: 0.3946 - val_acc: 0.8277
In [150]:
plt.figure(num=None, figsize=(8, 6), dpi=80)
plt.title('Funzione di Costo')
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.xlabel('Epoca')
plt.legend()
plt.show()
In [151]:
plt.figure(num=None, figsize=(8, 6), dpi=80)
plt.title('Accuracy')
plt.plot(history.history['acc'], label='train')
plt.plot(history.history['val_acc'], label='test')
plt.xlabel('Epoca')
plt.legend()
plt.show()
In [152]:
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score


y_classes = modello.predict_classes(test_set, verbose=0)

accuracy = accuracy_score(test_label2, y_classes)
print('Accuracy: %f' % accuracy)
precision = precision_score(test_label2, y_classes)
print('Precision: %f' % precision)
recall = recall_score(test_label2, y_classes)
print('Recall: %f' % recall)
f1 = f1_score(test_label2, y_classes)
print('F1 score: %f' % f1)
Accuracy: 0.827737
Precision: 0.682347
Recall: 0.425485
F1 score: 0.524138

Risultati in linea con quelli ottenuti con gli altri classificatori, solamente la recall migliora leggermente.

In [153]:
from sklearn.metrics import precision_recall_curve

precisions, recalls, thresholds = precision_recall_curve(test_label2, y_classes)

plt.figure(figsize=(8, 6))
plt.plot(recalls, precisions, "b-", linewidth=2)
plt.xlabel("Recall", fontsize=16)
plt.ylabel("Precision", fontsize=16)
plt.axis([0, 1, 0, 1])

plt.show()

Notiamo da questa curva, come sia difficile ottenere una buona recall e una buona precision allo stesso tempo, è come se fossere inversamente proporzionali.

In [154]:
from sklearn.metrics import roc_curve

fpr, tpr, thresholds = roc_curve(test_label2, y_classes)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, linewidth=2, label=label)
plt.plot([0, 1], [0, 1], 'k--')
plt.axis([0, 1, 0, 1])
plt.xlabel('False Positive Rate', fontsize=16)
plt.ylabel('True Positive Rate', fontsize=16)

plt.show()

Esattamente la stessa curva ROC presente nel miglior modello della classificazione.

In [155]:
from sklearn.metrics import confusion_matrix
import itertools

def plot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues):

    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt ='d'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    plt.show()


confusion = confusion_matrix(test_label2, y_classes)    
plot_confusion_matrix(confusion, classes=['Rain Tomorrow: NO', 'Rain Tomorrow: YES'], title='Confusion matrix')

Anche la matrice di confusione, ovviamente presenta dei risultati simili a quelli ottenuti con la classificazione.

Il vantaggio della rete neurale è che oltre alla classe fornisce la probabilità di pioggia, quindi un'utente può decidere, grazie al valore della probabilità, se fidarsi o meno di una determinata classificazione. Ecco di seguito un'esempio di output fornito dalla rete:

In [156]:
output=["No Rain Tomorrow","Rain Tomorrow"]
classe=modello.predict_classes(test_set[0:5], verbose=0)
prob=modello.predict(test_set[0:5],verbose=0)
for p,c in zip(prob,classe):
    print('Classificazione-> Classe: '+output[c]+', Probabilità: %.1f%s' % (p[c]*100,'%'))
Classificazione-> Classe: Rain Tomorrow, Probabilità: 91.1%
Classificazione-> Classe: No Rain Tomorrow, Probabilità: 77.0%
Classificazione-> Classe: No Rain Tomorrow, Probabilità: 96.3%
Classificazione-> Classe: No Rain Tomorrow, Probabilità: 80.9%
Classificazione-> Classe: No Rain Tomorrow, Probabilità: 81.5%

2.3.2 Post-processing

L'utilizzo della rete neurale non ha portato a delle migliorie rilevanti, per questo abbiamo eseguito le stesse operazioni di post-processing effettuate con la classificazione. Però, oltre all'utilizzo del dataset bilanciato, ho effettuato un'operazione sulle predizioni effettuate sul dataset orignale. Siccome la rete fornisce anche la probabilità e abbiamo problemi con la Recall, quindi con i falsi negativi, ho deciso di cambiare la classe 'no' in 'yes' qunado la rete è poco sicura. Ovvero quando la probabilità di 'no' è compresa tra il 50% e 60% cambia la classe in 'yes', perchè vuol dire che non è molto sicura su come classificare. Una volta fatta quest'operazione ho misurato le performance.

In [157]:
ymod_classes = modello.predict_classes(test_set, verbose=0)
ymod_probs = modello.predict(test_set, verbose=0)


index=0
for classe,prob in zip(ymod_classes,ymod_probs) :
    if classe==0 and (0.50<prob[0]<0.59) :
       ymod_classes[index]=1
       ymod_probs[index,1]=1-ymod_probs[index,1]
    index+=1

accuracy = accuracy_score(test_label2, ymod_classes)
print('Accuracy: %f' % accuracy)
precision = precision_score(test_label2, ymod_classes)
print('Precision: %f' % precision)
recall = recall_score(test_label2, ymod_classes)
print('Recall: %f' % recall)
f1 = f1_score(test_label2, ymod_classes)
print('F1 score: %f' % f1)
Accuracy: 0.824326
Precision: 0.627007
Recall: 0.523577
F1 score: 0.570643
In [158]:
confusion = confusion_matrix(test_label2, ymod_classes)    
plot_confusion_matrix(confusion, classes=['No Rain Tomorrow', 'Rain Tomorrow'], title='Confusion matrix')

Come previsto la recall aumenta quasi del 10% a discapito della precisione che perde circa il 5-6% in genere, mentre l'accuraci diminuisce di poco. Quindi questo metodo è molto efficace se si cerca una recall buona, quindi se all'utente interessa che vengano classificate bene le istanze della classe 'yes'.

Il resto del post-processing prosegue con il dataset bilanciato visto già nella classificazione.

In [201]:
from sklearn.utils import shuffle

def creaDatasetBilanciato(df, class_coloumn, num_elem=50000,bilancio=0.40):
    df_yes = df[df[class_coloumn] == 1]  
    df_no = df[df[class_coloumn] == 0]
    sample_yes = int(num_elem*bilancio)
    sample_no =  int(num_elem*(1-bilancio))
    if df_yes.shape[0]<sample_yes:
        return None
    df_yes = df_yes.sample(n=sample_yes)
    df_no = df_no.sample(n=sample_no)
    df_new = pd.concat([df_yes, df_no])
    df_new = shuffle(df_new, random_state=1)
    return df_new
In [202]:
new_df=creaDatasetBilanciato(df,'RainTomorrow',num_elem=90000,bilancio=0.35)
if not isinstance(new_df,pd.DataFrame) :
    print("Non proseguire, cambia i parametri!")
In [203]:
training_set, test_set = train_test_split(new_df, test_size=0.2, random_state=42)

training_set["RainTomorrow"].hist()
Out[203]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fddb8461ac8>
In [204]:
test_set["RainTomorrow"].hist()
Out[204]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fddb840b3c8>
In [205]:
training_label=training_set['RainTomorrow'].copy()
training_set=training_set.drop("RainTomorrow",axis=1)
test_label=test_set['RainTomorrow'].copy()
test_set=test_set.drop('RainTomorrow',axis=1)
In [206]:
test_label2=test_label.copy()

#one-hot encode labels
training_label=to_categorical(training_label)
test_label=to_categorical(test_label)

La rete è uguale, ovviamente, a quella usata in precedenza. L'unica differenza è che ora utilizzo la regolarizzazione di tipo l2 per evitare l'ioverfitting. Dato che adesso ho meno dati e quindi la rete "fatica" un po' di più ad apprendere.

In [207]:
modello=keras.Sequential()
parametroRegolarizzazione=0.0105
modello.add(keras.layers.Dense(units=4,kernel_regularizer=keras.regularizers.l2(parametroRegolarizzazione),activation='relu',input_shape=(9,)))
modello.add(keras.layers.Dense(units=4,kernel_regularizer=keras.regularizers.l2(parametroRegolarizzazione),activation='relu'))
modello.add(keras.layers.Dense(units=2,activation='softmax'))

epoche=175
learning_rate=0.01
batch=1024

modello.compile(optimizer=keras.optimizers.SGD(learning_rate),
                    loss='categorical_crossentropy',
                    metrics=['accuracy']
                    )

history=modello.fit(training_set,training_label, validation_data=(test_set, test_label),epochs=epoche,batch_size=batch)
Train on 71999 samples, validate on 18000 samples
Epoch 1/175
71999/71999 [==============================] - 1s 9us/step - loss: 0.8245 - acc: 0.5738 - val_loss: 0.7655 - val_acc: 0.6296
Epoch 2/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.7593 - acc: 0.6276 - val_loss: 0.7507 - val_acc: 0.6349
Epoch 3/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.7457 - acc: 0.6311 - val_loss: 0.7375 - val_acc: 0.6365
Epoch 4/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.7332 - acc: 0.6339 - val_loss: 0.7256 - val_acc: 0.6407
Epoch 5/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.7224 - acc: 0.6384 - val_loss: 0.7156 - val_acc: 0.6438
Epoch 6/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.7133 - acc: 0.6449 - val_loss: 0.7070 - val_acc: 0.6493
Epoch 7/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.7055 - acc: 0.6520 - val_loss: 0.6995 - val_acc: 0.6546
Epoch 8/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6985 - acc: 0.6594 - val_loss: 0.6927 - val_acc: 0.6632
Epoch 9/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6921 - acc: 0.6675 - val_loss: 0.6863 - val_acc: 0.6691
Epoch 10/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6860 - acc: 0.6738 - val_loss: 0.6802 - val_acc: 0.6783
Epoch 11/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6800 - acc: 0.6809 - val_loss: 0.6742 - val_acc: 0.6841
Epoch 12/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6739 - acc: 0.6871 - val_loss: 0.6682 - val_acc: 0.6908
Epoch 13/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6677 - acc: 0.6937 - val_loss: 0.6616 - val_acc: 0.6975
Epoch 14/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6611 - acc: 0.6998 - val_loss: 0.6549 - val_acc: 0.7032
Epoch 15/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6540 - acc: 0.7070 - val_loss: 0.6475 - val_acc: 0.7108
Epoch 16/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6465 - acc: 0.7127 - val_loss: 0.6398 - val_acc: 0.7153
Epoch 17/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6384 - acc: 0.7173 - val_loss: 0.6317 - val_acc: 0.7182
Epoch 18/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6300 - acc: 0.7226 - val_loss: 0.6233 - val_acc: 0.7246
Epoch 19/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6211 - acc: 0.7258 - val_loss: 0.6143 - val_acc: 0.7274
Epoch 20/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6114 - acc: 0.7289 - val_loss: 0.6046 - val_acc: 0.7312
Epoch 21/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.6010 - acc: 0.7319 - val_loss: 0.5944 - val_acc: 0.7333
Epoch 22/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5900 - acc: 0.7337 - val_loss: 0.5841 - val_acc: 0.7343
Epoch 23/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5794 - acc: 0.7364 - val_loss: 0.5746 - val_acc: 0.7373
Epoch 24/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5703 - acc: 0.7421 - val_loss: 0.5669 - val_acc: 0.7451
Epoch 25/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5627 - acc: 0.7513 - val_loss: 0.5605 - val_acc: 0.7527
Epoch 26/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5566 - acc: 0.7607 - val_loss: 0.5552 - val_acc: 0.7587
Epoch 27/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5515 - acc: 0.7664 - val_loss: 0.5512 - val_acc: 0.7623
Epoch 28/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5475 - acc: 0.7690 - val_loss: 0.5479 - val_acc: 0.7639
Epoch 29/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5442 - acc: 0.7712 - val_loss: 0.5450 - val_acc: 0.7658
Epoch 30/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5413 - acc: 0.7725 - val_loss: 0.5427 - val_acc: 0.7670
Epoch 31/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5389 - acc: 0.7734 - val_loss: 0.5409 - val_acc: 0.7669
Epoch 32/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5368 - acc: 0.7743 - val_loss: 0.5387 - val_acc: 0.7700
Epoch 33/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5348 - acc: 0.7743 - val_loss: 0.5369 - val_acc: 0.7703
Epoch 34/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5330 - acc: 0.7746 - val_loss: 0.5353 - val_acc: 0.7707
Epoch 35/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5313 - acc: 0.7752 - val_loss: 0.5337 - val_acc: 0.7718
Epoch 36/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5297 - acc: 0.7753 - val_loss: 0.5322 - val_acc: 0.7714
Epoch 37/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5282 - acc: 0.7756 - val_loss: 0.5309 - val_acc: 0.7712
Epoch 38/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5267 - acc: 0.7758 - val_loss: 0.5294 - val_acc: 0.7721
Epoch 39/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5253 - acc: 0.7760 - val_loss: 0.5281 - val_acc: 0.7731
Epoch 40/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5240 - acc: 0.7760 - val_loss: 0.5268 - val_acc: 0.7731
Epoch 41/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5227 - acc: 0.7763 - val_loss: 0.5256 - val_acc: 0.7738
Epoch 42/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5214 - acc: 0.7763 - val_loss: 0.5244 - val_acc: 0.7738
Epoch 43/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5202 - acc: 0.7768 - val_loss: 0.5233 - val_acc: 0.7738
Epoch 44/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5190 - acc: 0.7769 - val_loss: 0.5220 - val_acc: 0.7739
Epoch 45/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5179 - acc: 0.7768 - val_loss: 0.5212 - val_acc: 0.7739
Epoch 46/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5168 - acc: 0.7767 - val_loss: 0.5198 - val_acc: 0.7742
Epoch 47/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5157 - acc: 0.7766 - val_loss: 0.5188 - val_acc: 0.7736
Epoch 48/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5147 - acc: 0.7771 - val_loss: 0.5178 - val_acc: 0.7742
Epoch 49/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5137 - acc: 0.7774 - val_loss: 0.5170 - val_acc: 0.7732
Epoch 50/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5127 - acc: 0.7770 - val_loss: 0.5159 - val_acc: 0.7743
Epoch 51/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5118 - acc: 0.7772 - val_loss: 0.5149 - val_acc: 0.7746
Epoch 52/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5108 - acc: 0.7776 - val_loss: 0.5143 - val_acc: 0.7753
Epoch 53/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5099 - acc: 0.7776 - val_loss: 0.5131 - val_acc: 0.7745
Epoch 54/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5090 - acc: 0.7776 - val_loss: 0.5129 - val_acc: 0.7752
Epoch 55/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5082 - acc: 0.7774 - val_loss: 0.5114 - val_acc: 0.7746
Epoch 56/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5074 - acc: 0.7777 - val_loss: 0.5108 - val_acc: 0.7753
Epoch 57/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5065 - acc: 0.7782 - val_loss: 0.5098 - val_acc: 0.7743
Epoch 58/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5058 - acc: 0.7782 - val_loss: 0.5092 - val_acc: 0.7747
Epoch 59/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5050 - acc: 0.7783 - val_loss: 0.5084 - val_acc: 0.7752
Epoch 60/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5043 - acc: 0.7781 - val_loss: 0.5078 - val_acc: 0.7752
Epoch 61/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5036 - acc: 0.7787 - val_loss: 0.5068 - val_acc: 0.7748
Epoch 62/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5029 - acc: 0.7788 - val_loss: 0.5062 - val_acc: 0.7747
Epoch 63/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5022 - acc: 0.7789 - val_loss: 0.5055 - val_acc: 0.7750
Epoch 64/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5015 - acc: 0.7786 - val_loss: 0.5049 - val_acc: 0.7753
Epoch 65/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5009 - acc: 0.7786 - val_loss: 0.5042 - val_acc: 0.7749
Epoch 66/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.5003 - acc: 0.7788 - val_loss: 0.5036 - val_acc: 0.7752
Epoch 67/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4996 - acc: 0.7788 - val_loss: 0.5031 - val_acc: 0.7754
Epoch 68/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4991 - acc: 0.7792 - val_loss: 0.5025 - val_acc: 0.7754
Epoch 69/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4985 - acc: 0.7793 - val_loss: 0.5019 - val_acc: 0.7753
Epoch 70/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4979 - acc: 0.7788 - val_loss: 0.5014 - val_acc: 0.7754
Epoch 71/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4974 - acc: 0.7796 - val_loss: 0.5009 - val_acc: 0.7756
Epoch 72/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4969 - acc: 0.7796 - val_loss: 0.5004 - val_acc: 0.7756
Epoch 73/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4964 - acc: 0.7796 - val_loss: 0.4997 - val_acc: 0.7759
Epoch 74/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4959 - acc: 0.7795 - val_loss: 0.4996 - val_acc: 0.7764
Epoch 75/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4954 - acc: 0.7794 - val_loss: 0.4988 - val_acc: 0.7760
Epoch 76/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4949 - acc: 0.7801 - val_loss: 0.4984 - val_acc: 0.7758
Epoch 77/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4944 - acc: 0.7797 - val_loss: 0.4993 - val_acc: 0.7762
Epoch 78/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4940 - acc: 0.7799 - val_loss: 0.4977 - val_acc: 0.7758
Epoch 79/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4936 - acc: 0.7799 - val_loss: 0.4971 - val_acc: 0.7760
Epoch 80/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4932 - acc: 0.7802 - val_loss: 0.4967 - val_acc: 0.7761
Epoch 81/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4927 - acc: 0.7801 - val_loss: 0.4962 - val_acc: 0.7764
Epoch 82/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4924 - acc: 0.7800 - val_loss: 0.4960 - val_acc: 0.7769
Epoch 83/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4920 - acc: 0.7802 - val_loss: 0.4956 - val_acc: 0.7763
Epoch 84/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4916 - acc: 0.7801 - val_loss: 0.4951 - val_acc: 0.7761
Epoch 85/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4913 - acc: 0.7802 - val_loss: 0.4950 - val_acc: 0.7765
Epoch 86/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4909 - acc: 0.7802 - val_loss: 0.4945 - val_acc: 0.7766
Epoch 87/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4906 - acc: 0.7801 - val_loss: 0.4941 - val_acc: 0.7772
Epoch 88/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4902 - acc: 0.7801 - val_loss: 0.4939 - val_acc: 0.7773
Epoch 89/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4899 - acc: 0.7801 - val_loss: 0.4934 - val_acc: 0.7766
Epoch 90/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4896 - acc: 0.7805 - val_loss: 0.4932 - val_acc: 0.7773
Epoch 91/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4893 - acc: 0.7808 - val_loss: 0.4931 - val_acc: 0.7771
Epoch 92/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4890 - acc: 0.7804 - val_loss: 0.4925 - val_acc: 0.7769
Epoch 93/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4887 - acc: 0.7804 - val_loss: 0.4922 - val_acc: 0.7771
Epoch 94/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4884 - acc: 0.7804 - val_loss: 0.4925 - val_acc: 0.7771
Epoch 95/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4881 - acc: 0.7802 - val_loss: 0.4918 - val_acc: 0.7759
Epoch 96/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4879 - acc: 0.7804 - val_loss: 0.4924 - val_acc: 0.7768
Epoch 97/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4876 - acc: 0.7805 - val_loss: 0.4912 - val_acc: 0.7769
Epoch 98/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4873 - acc: 0.7806 - val_loss: 0.4911 - val_acc: 0.7763
Epoch 99/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4871 - acc: 0.7807 - val_loss: 0.4906 - val_acc: 0.7779
Epoch 100/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4869 - acc: 0.7805 - val_loss: 0.4910 - val_acc: 0.7774
Epoch 101/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4867 - acc: 0.7804 - val_loss: 0.4903 - val_acc: 0.7779
Epoch 102/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4864 - acc: 0.7801 - val_loss: 0.4900 - val_acc: 0.7773
Epoch 103/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4861 - acc: 0.7802 - val_loss: 0.4897 - val_acc: 0.7772
Epoch 104/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4860 - acc: 0.7807 - val_loss: 0.4903 - val_acc: 0.7775
Epoch 105/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4858 - acc: 0.7803 - val_loss: 0.4893 - val_acc: 0.7770
Epoch 106/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4855 - acc: 0.7804 - val_loss: 0.4891 - val_acc: 0.7772
Epoch 107/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4854 - acc: 0.7806 - val_loss: 0.4891 - val_acc: 0.7778
Epoch 108/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4853 - acc: 0.7806 - val_loss: 0.4888 - val_acc: 0.7781
Epoch 109/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4850 - acc: 0.7808 - val_loss: 0.4887 - val_acc: 0.7772
Epoch 110/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4848 - acc: 0.7804 - val_loss: 0.4884 - val_acc: 0.7773
Epoch 111/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4846 - acc: 0.7804 - val_loss: 0.4884 - val_acc: 0.7767
Epoch 112/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4845 - acc: 0.7808 - val_loss: 0.4882 - val_acc: 0.7774
Epoch 113/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4843 - acc: 0.7803 - val_loss: 0.4879 - val_acc: 0.7773
Epoch 114/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4841 - acc: 0.7809 - val_loss: 0.4883 - val_acc: 0.7781
Epoch 115/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4840 - acc: 0.7808 - val_loss: 0.4876 - val_acc: 0.7778
Epoch 116/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4838 - acc: 0.7807 - val_loss: 0.4874 - val_acc: 0.7780
Epoch 117/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4837 - acc: 0.7804 - val_loss: 0.4877 - val_acc: 0.7761
Epoch 118/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4836 - acc: 0.7806 - val_loss: 0.4873 - val_acc: 0.7772
Epoch 119/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4834 - acc: 0.7805 - val_loss: 0.4870 - val_acc: 0.7780
Epoch 120/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4833 - acc: 0.7804 - val_loss: 0.4870 - val_acc: 0.7771
Epoch 121/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4831 - acc: 0.7810 - val_loss: 0.4867 - val_acc: 0.7781
Epoch 122/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4830 - acc: 0.7806 - val_loss: 0.4866 - val_acc: 0.7779
Epoch 123/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4829 - acc: 0.7809 - val_loss: 0.4865 - val_acc: 0.7783
Epoch 124/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4827 - acc: 0.7808 - val_loss: 0.4863 - val_acc: 0.7777
Epoch 125/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4826 - acc: 0.7807 - val_loss: 0.4864 - val_acc: 0.7769
Epoch 126/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4825 - acc: 0.7807 - val_loss: 0.4862 - val_acc: 0.7781
Epoch 127/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4823 - acc: 0.7810 - val_loss: 0.4860 - val_acc: 0.7777
Epoch 128/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4822 - acc: 0.7804 - val_loss: 0.4859 - val_acc: 0.7781
Epoch 129/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4821 - acc: 0.7811 - val_loss: 0.4858 - val_acc: 0.7781
Epoch 130/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4820 - acc: 0.7810 - val_loss: 0.4857 - val_acc: 0.7784
Epoch 131/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4819 - acc: 0.7811 - val_loss: 0.4863 - val_acc: 0.7779
Epoch 132/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4818 - acc: 0.7802 - val_loss: 0.4854 - val_acc: 0.7777
Epoch 133/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4817 - acc: 0.7808 - val_loss: 0.4855 - val_acc: 0.7777
Epoch 134/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4816 - acc: 0.7803 - val_loss: 0.4856 - val_acc: 0.7768
Epoch 135/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4815 - acc: 0.7810 - val_loss: 0.4854 - val_acc: 0.7781
Epoch 136/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4814 - acc: 0.7810 - val_loss: 0.4851 - val_acc: 0.7775
Epoch 137/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4812 - acc: 0.7811 - val_loss: 0.4852 - val_acc: 0.7777
Epoch 138/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4812 - acc: 0.7807 - val_loss: 0.4850 - val_acc: 0.7773
Epoch 139/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4812 - acc: 0.7808 - val_loss: 0.4848 - val_acc: 0.7786
Epoch 140/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4810 - acc: 0.7811 - val_loss: 0.4851 - val_acc: 0.7782
Epoch 141/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4809 - acc: 0.7807 - val_loss: 0.4849 - val_acc: 0.7782
Epoch 142/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4809 - acc: 0.7809 - val_loss: 0.4846 - val_acc: 0.7776
Epoch 143/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4808 - acc: 0.7809 - val_loss: 0.4845 - val_acc: 0.7787
Epoch 144/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4807 - acc: 0.7807 - val_loss: 0.4845 - val_acc: 0.7776
Epoch 145/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4806 - acc: 0.7809 - val_loss: 0.4851 - val_acc: 0.7773
Epoch 146/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4806 - acc: 0.7808 - val_loss: 0.4842 - val_acc: 0.7788
Epoch 147/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4804 - acc: 0.7807 - val_loss: 0.4843 - val_acc: 0.7782
Epoch 148/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4804 - acc: 0.7805 - val_loss: 0.4841 - val_acc: 0.7787
Epoch 149/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4803 - acc: 0.7808 - val_loss: 0.4842 - val_acc: 0.7779
Epoch 150/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4802 - acc: 0.7808 - val_loss: 0.4852 - val_acc: 0.7766
Epoch 151/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4802 - acc: 0.7811 - val_loss: 0.4840 - val_acc: 0.7770
Epoch 152/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4801 - acc: 0.7812 - val_loss: 0.4838 - val_acc: 0.7781
Epoch 153/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4801 - acc: 0.7807 - val_loss: 0.4838 - val_acc: 0.7787
Epoch 154/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4800 - acc: 0.7812 - val_loss: 0.4837 - val_acc: 0.7786
Epoch 155/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4799 - acc: 0.7814 - val_loss: 0.4836 - val_acc: 0.7787
Epoch 156/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4799 - acc: 0.7809 - val_loss: 0.4836 - val_acc: 0.7792
Epoch 157/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4798 - acc: 0.7808 - val_loss: 0.4836 - val_acc: 0.7774
Epoch 158/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4798 - acc: 0.7809 - val_loss: 0.4835 - val_acc: 0.7778
Epoch 159/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4797 - acc: 0.7811 - val_loss: 0.4834 - val_acc: 0.7791
Epoch 160/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4796 - acc: 0.7811 - val_loss: 0.4833 - val_acc: 0.7793
Epoch 161/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4795 - acc: 0.7807 - val_loss: 0.4841 - val_acc: 0.7778
Epoch 162/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4795 - acc: 0.7808 - val_loss: 0.4833 - val_acc: 0.7773
Epoch 163/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4794 - acc: 0.7812 - val_loss: 0.4835 - val_acc: 0.7776
Epoch 164/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4794 - acc: 0.7810 - val_loss: 0.4831 - val_acc: 0.7789
Epoch 165/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4793 - acc: 0.7810 - val_loss: 0.4830 - val_acc: 0.7793
Epoch 166/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4792 - acc: 0.7813 - val_loss: 0.4832 - val_acc: 0.7779
Epoch 167/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4792 - acc: 0.7812 - val_loss: 0.4831 - val_acc: 0.7779
Epoch 168/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4791 - acc: 0.7811 - val_loss: 0.4829 - val_acc: 0.7785
Epoch 169/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4791 - acc: 0.7807 - val_loss: 0.4829 - val_acc: 0.7789
Epoch 170/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4791 - acc: 0.7811 - val_loss: 0.4828 - val_acc: 0.7788
Epoch 171/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4790 - acc: 0.7811 - val_loss: 0.4828 - val_acc: 0.7779
Epoch 172/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4789 - acc: 0.7813 - val_loss: 0.4827 - val_acc: 0.7781
Epoch 173/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4789 - acc: 0.7810 - val_loss: 0.4827 - val_acc: 0.7782
Epoch 174/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4788 - acc: 0.7808 - val_loss: 0.4830 - val_acc: 0.7760
Epoch 175/175
71999/71999 [==============================] - 0s 3us/step - loss: 0.4788 - acc: 0.7809 - val_loss: 0.4826 - val_acc: 0.7791
In [208]:
plt.figure(num=None, figsize=(8, 6), dpi=80)
plt.title('Funzione di Costo')
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.xlabel('Epoca')
plt.legend()
plt.show()
In [209]:
plt.figure(num=None, figsize=(8, 6), dpi=80)
plt.title('Accuracy')
plt.plot(history.history['acc'], label='train')
plt.plot(history.history['val_acc'], label='test')
plt.xlabel('Epoca')
plt.legend()
plt.show()
In [210]:
y_classes = modello.predict_classes(test_set, verbose=0)

accuracy = accuracy_score(test_label2, y_classes)
print('Accuracy: %f' % accuracy)
precision = precision_score(test_label2, y_classes)
print('Precision: %f' % precision)
recall = recall_score(test_label2, y_classes)
print('Recall: %f' % recall)
f1 = f1_score(test_label2, y_classes)
print('F1 score: %f' % f1)
Accuracy: 0.779056
Precision: 0.726374
Recall: 0.586245
F1 score: 0.648830
In [211]:
precisions, recalls, thresholds = precision_recall_curve(test_label2, y_classes)

plt.figure(figsize=(8, 6))
plt.plot(recalls, precisions, "b-", linewidth=2)
plt.xlabel("Recall", fontsize=16)
plt.ylabel("Precision", fontsize=16)
plt.axis([0, 1, 0, 1])

plt.show()
In [212]:
fpr, tpr, thresholds = roc_curve(test_label2, y_classes)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, linewidth=2, label=label)
plt.plot([0, 1], [0, 1], 'k--')
plt.axis([0, 1, 0, 1])
plt.xlabel('False Positive Rate', fontsize=16)
plt.ylabel('True Positive Rate', fontsize=16)

plt.show()
In [213]:
confusion = confusion_matrix(test_label2, y_classes)    
plot_confusion_matrix(confusion, classes=['No Rain Tomorrow', 'Rain Tomorrow'], title='Confusion matrix')
In [214]:
output=["No Rain Tomorrow","Rain Tomorrow"]
classe=modello.predict_classes(test_set[0:5], verbose=0)
prob=modello.predict(test_set[0:5],verbose=0)
for p,c in zip(prob,classe):
    print('Classificazione-> Classe: '+output[c]+', Probabilità: %.1f%s' % (p[c]*100,'%'))
Classificazione-> Classe: No Rain Tomorrow, Probabilità: 92.4%
Classificazione-> Classe: No Rain Tomorrow, Probabilità: 91.7%
Classificazione-> Classe: No Rain Tomorrow, Probabilità: 94.2%
Classificazione-> Classe: No Rain Tomorrow, Probabilità: 96.0%
Classificazione-> Classe: Rain Tomorrow, Probabilità: 73.3%
In [215]:
ymod_classes = modello.predict_classes(test_set, verbose=0)
ymod_probs = modello.predict(test_set, verbose=0)


index=0
for classe,prob in zip(ymod_classes,ymod_probs) :
    if classe==0 and (0.50<prob[0]<0.59) :
       ymod_classes[index]=1
       ymod_probs[index,1]=1-ymod_probs[index,1]
    index+=1

accuracy = accuracy_score(test_label2, ymod_classes)
print('Accuracy: %f' % accuracy)
precision = precision_score(test_label2, ymod_classes)
print('Precision: %f' % precision)
recall = recall_score(test_label2, ymod_classes)
print('Recall: %f' % recall)
f1 = f1_score(test_label2, ymod_classes)
print('F1 score: %f' % f1)
Accuracy: 0.771111
Precision: 0.669028
Recall: 0.677996
F1 score: 0.673482
In [216]:
confusion = confusion_matrix(test_label2, ymod_classes)    
plot_confusion_matrix(confusion, classes=['No Rain Tomorrow', 'Rain Tomorrow'], title='Confusion matrix')

Conclusioni

Nell’analizzare il dataset ci si è soffermati molto nella fase di preprocessing. Oltre ad analizzare il comportamento e la correlazione di ogni attributo rispetto all’attributo di classe, vengono trasformati gli attributi categorici e ordinali in float applicando varie tecniche offerte dalle librerie di python. Alcuni attributi vengono eliminati perché hanno troppi valori null e di conseguenza non incidono nella classificazione, mentre altri sono fortemente correlati, ad esempio temp9am e minTemp mostrano quasi gli stessi valori e quindi temp9am è stata eliminata. Quest’ultimi sono dati dal fatto che probabilmente i risultati di una rilevazione temporale vengono memorizzate in entrambe le colonne (la temperatura minima è anche quella rilevata alle 9 di mattina). Dopo aver finito il preprocessing si è iniziato con la classificazione, tuttavia si è provato a applicare diverse scelte sugli attributi per provare a migliorare i parametri sulla classificazione, di queste, l’unico cambiamento che ha davvero migliorato lo score è stato la creazione di un dataset bilanciato. Quest’ultimo cambiamento si è applicato perché nel dataset originale c’è un altissimo numero di record con etichetta ‘no’ e pochi con etichetta ‘si’, dunque si è creato un dataset che contiene il 35% di record con etichetta ‘si’, questo ha fatto si che le prestazioni aumentassero in modo significativo.

Gli algoritmi di classificazione utilizzati sono stati: Decision Tree, Naive Bayes, Support Vector Machine e Support Vector Machine con discesa del gradiente, KnearestNeighbor e, per finire, alcuni ensamble learning, quali: RandomForest, Voting e Bagging. I valori di picco per le misure calcolati nella cross validation sono:

• Accuracy: 78% (tutti i classificatori a meno del Decision Tree (76%))
• Precision: 76% (Voting e Knn)
• Recall: 60% (RandomForest e SVC)
• F-Measure: 65% (tutti i classificatori meno DecisionTree e NaiveBabyes)

Per quando riguarda le prestazioni medie, il bagging sembra essere il più equilibrato.

Si è provato ad applicare anche le tecniche di clustering sui dati, dapprima si è provato ad identificare le aree geografiche, ma la mancanza di informazioni non ha permesso una buona clusterizzazione. Si è allora provveduto a provare di modellare i punti del dataset preprocessato in cluster, così da poter applicare al risultato alcuni algoritmi di classificazione nella speranza di poter migliorare gli scores. Putroppo la clusterizzazione non ha dato i risultati sperati. Infine la rete neurale, considerando il dataset bilanciato, presenta più o meno gli stessi valori dei classificatori migliori, però come output fornisce un'informazione aggiuntiva che è la probabilità. Proprio grazie alla probabilità fornita dalla rete che si può modellare ulteriormente il modello, infatti cambiando il risultato della classificazione basandosi sui valori basis che presenta la probabilità, è possibile ottenere dei valori di recall pari al 68%, anche se la presione diminuisce (ma di poco). Nessun classificatore oltre alla rete permette di fare questo. Concludo dicendo che la rete neurale è più versatile e permette all'utente di avere una misura di quanto sia affidabile quella determinata calssificazione.